From 353d74cec0bf0933eefd325c1d96bbad8aec310e Mon Sep 17 00:00:00 2001 From: zichun Date: Fri, 8 May 2026 15:32:36 +0800 Subject: [PATCH] Add maintainer running-locally + tech-stack + notifications pages --- en/docs/api-reference/index.md | 1 + en/docs/api-reference/notifications.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ en/docs/concepts/api-surface.md | 2 +- en/docs/concepts/index.md | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ en/docs/index.md | 1 + en/docs/reference/maintainer/index.md | 2 ++ en/docs/reference/maintainer/running-locally.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ en/docs/reference/maintainer/tech-stack.md | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ en/mkdocs.yml | 3 +++ zh/docs/api-reference/external.md | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ zh/docs/api-reference/index.md | 21 +++++++++++++++++++++ zh/docs/api-reference/internal.md | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ zh/docs/api-reference/messaging.md | 38 ++++++++++++++++++++++++++++++++++++++ zh/docs/api-reference/notifications.md | 38 ++++++++++++++++++++++++++++++++++++++ zh/docs/api-reference/webhooks.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ zh/docs/concepts/api-surface.md | 32 ++++++++++++++++++++++++++++++++ zh/docs/concepts/index.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ zh/docs/index.md | 1 + zh/docs/reference/maintainer/index.md | 1 + zh/docs/reference/maintainer/running-locally.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ zh/mkdocs.yml | 15 ++++++++++++--- 21 files changed, 917 insertions(+), 4 deletions(-) create mode 100644 en/docs/api-reference/notifications.md create mode 100644 en/docs/reference/maintainer/running-locally.md create mode 100644 en/docs/reference/maintainer/tech-stack.md create mode 100644 zh/docs/api-reference/external.md create mode 100644 zh/docs/api-reference/index.md create mode 100644 zh/docs/api-reference/internal.md create mode 100644 zh/docs/api-reference/messaging.md create mode 100644 zh/docs/api-reference/notifications.md create mode 100644 zh/docs/api-reference/webhooks.md create mode 100644 zh/docs/concepts/api-surface.md create mode 100644 zh/docs/reference/maintainer/running-locally.md diff --git a/en/docs/api-reference/index.md b/en/docs/api-reference/index.md index 3654037..3d917d6 100644 --- a/en/docs/api-reference/index.md +++ b/en/docs/api-reference/index.md @@ -10,6 +10,7 @@ overview lives in [concepts/api-surface](../concepts/api-surface.md). | [External API](external.md) | `xlyApi` | `/xlyApi` | You are integrating an outside system that calls xly. | | [Webhooks](webhooks.md) | `xlyInterface` | `/xlyInterface` | A third-party system needs to push events into xly. | | [Messaging](messaging.md) | `xlyEntry` + `xlyErpJms*` | n/a (ActiveMQ / RocketMQ) | An asynchronous, fan-out integration is more appropriate than a synchronous HTTP call. | +| [Notifications](notifications.md) | `xlyMsg` (consumed as a library by `xlyEntry`, `xlyBusinessService`, `xlyInterface`) | n/a (DingTalk / WeChat APIs) | A business event needs to push a chat-platform message to a user. | ## Reading order diff --git a/en/docs/api-reference/notifications.md b/en/docs/api-reference/notifications.md new file mode 100644 index 0000000..e8261a6 --- /dev/null +++ b/en/docs/api-reference/notifications.md @@ -0,0 +1,54 @@ +# Notifications (xlyMsg) + +Outbound notifications — DingTalk and WeChat — go through the `xlyMsg` +module. This is *not* an HTTP surface that callers hit; it's an +internal SDK that in-scope services call when a business event should +push a message out to a chat platform. + +## What's inside + +`xlyMsg/src/main/java/com/xly/`: + +| Package | Role | +|---|---| +| `dingtalk/service/DingTalkService` + `dingtalk/util/SendDingTalkUtil`, `DingTalkMsgContentUtil`, `LocalCacheClient` | DingTalk corp-message dispatch. Wraps `com.aliyun:dingtalk:2.1.14` and `com.aliyun:alibaba-dingtalk-service-sdk:2.0.0`. | +| `wechat/service/WechatService` + `wechat/util/SendWxUtil`, `Wx_SignatureUtil`, `JedisMsgUtil`, `MsgContentUtil`, `Xml2JsonUtil` | WeChat work-platform dispatch — signature + send, including a Redis-backed access-token cache. | +| `notice/service/NoticeService` | Provider-agnostic notice abstraction; routes a logical "notify user X about event Y" to the right backend. | + +`xlyMsg/build.gradle` — sole framework dependency is `xlyPersist`. The +module is consumed as a library, not deployed as its own service. + +## Who calls it (in-scope callers) + +This is what makes `xlyMsg` framework-relevant rather than plat-tier: + +| Caller | What it does | +|---|---| +| `xlyEntry/.../web/businessweb/TestController.java` | Diagnostic endpoint to fire a test DingTalk message. | +| `xlyBusinessService/.../thread/UpdateDingTalkThread.java` | Async worker that pushes DingTalk updates after document changes. | +| `xlyBusinessService/.../service/impl/CheckExamineFlowServiceImpl.java` | Workflow approval notifications — when an Activiti task is reassigned or completed, the assignee gets a chat message. | +| `xlyBusinessService/.../service/impl/GenericProcedureCallServiceImpl.java` | Generic-proc hook: any `gdsmodule` proc that opts in can publish a notice through this path. | +| `xlyInterface/.../util/DingTalkUtil.java` + `scheduler/ScheduledTasks.java`, `ErpJobRunStatus.java` | Scheduled-job heartbeat / failure alerts on the integration side. | + +## Configuration + +Credentials (DingTalk corp-app key/secret, WeChat AppID, agent IDs) are +read from environment-specific yaml profiles. There is no global +default — each customer deployment fills its own values, which is why +the framework wiki cannot give a canonical example. Look at the active +profile yaml under `xlyEntry/src/main/resources/application-.yml` +for the keys actually consumed. + +The WeChat path uses Redis for access-token caching +(`wechat/util/JedisMsgUtil.java`) — same Redis instance the rest of +the framework uses. See [Multi-service deployment](../reference/maintainer/deployment.md) +for the broader picture of which services share which Redis. + +## What this is not + +- **Not** an inbound webhook receiver. DingTalk callbacks (e.g., + approval-button clicks) go through `xlyInterface` with the rest of + the [webhooks](webhooks.md). +- **Not** the `xlyPlat*` push-notification path. The plat tier has + its own channel (`xlyPlatSmsService`, JPush, app-side push) that + this wiki treats as out of scope. diff --git a/en/docs/concepts/api-surface.md b/en/docs/concepts/api-surface.md index f486bc8..d2b337b 100644 --- a/en/docs/concepts/api-surface.md +++ b/en/docs/concepts/api-surface.md @@ -35,7 +35,7 @@ sacrifice clarity: ## What each tier looks like at runtime -- **Internal** — see [the four-table read](request-lifecycle.md). One +- **Internal** — see [the four-table read](../reference/maintainer/runtime.md#the-four-table-read). One endpoint (`/business/getModelBysId`) returns the entire form layout; another (`/business/addUpdateDelBusinessData`) writes any row in any table the metadata names. Few endpoints, generic shapes. diff --git a/en/docs/concepts/index.md b/en/docs/concepts/index.md index b7010d4..5a860d1 100644 --- a/en/docs/concepts/index.md +++ b/en/docs/concepts/index.md @@ -7,6 +7,64 @@ a customer sees is a row in `gdsmodule` joined to layout rows in `gdsconfigformmaster/slave`, permission rows in `gdsjurisdiction`, and stored procedures invoked through `BusinessBaseController` in the runtime. +## At a glance + +```mermaid +flowchart TB + classDef oos stroke-dasharray:5 5,color:#999 + + BACK["BACK
builder UI"] + FROUNT["FROUNT
customer UI"] + EXT([External integrators]) + HOOKS([Third-party webhooks]) + + subgraph framework [Framework runtime - in scope] + XENTRY[xlyEntry] + XAPI[xlyApi] + XIF[xlyInterface] + XFLOW[xlyFlow] + XPLC[xlyPlc] + XMSG[/"xlyMsg
library"/] + end + + DB[("MySQL
xlyweberp")] + REDIS[(Redis)] + AMQ([ActiveMQ]) + XEJMSC[xlyErpJmsConsumer] + PLAT[("xlyPlat* modules
+ MongoDB")]:::oos + + BACK --> XENTRY + FROUNT --> XENTRY + FROUNT --> XAPI + EXT --> XAPI + HOOKS --> XIF + + XENTRY --> DB + XAPI --> DB + XIF --> DB + XFLOW --> DB + XPLC --> DB + + XENTRY <--> REDIS + XENTRY -- "metadata change" --> AMQ + AMQ --> XEJMSC + XEJMSC --> REDIS + + XENTRY -. uses .-> XMSG + XIF -. uses .-> XMSG + + PLAT -.-> DB +``` + +The dashed cluster (`xlyPlat*` + MongoDB) is the B2B printing-platform +tier — present in the build, but [out of scope](../index.md#whats-out-of-scope) +for this wiki. + +For the library inventory behind each box, see the +[Tech stack](../reference/maintainer/tech-stack.md) page. + +## Concept pages + These pages are intentionally short — each one explains one concept, with links to the [vertical slices](../slices/index.md) that exercise it. Concepts pages should stay short; if one grows past a screenful, that's diff --git a/en/docs/index.md b/en/docs/index.md index 8bdb31c..20b4e2e 100644 --- a/en/docs/index.md +++ b/en/docs/index.md @@ -38,6 +38,7 @@ which Reference chapter you go deep on. - Face recognition (`xlyFace`) — niche. - Per-tenant schema drift between `xlyweberp_*` databases — wiki targets one schema. - Backup tables (`*_bak`, `*0302`, etc.). +- The MongoDB document store (`spring.data.mongodb.uri` in the yaml profiles, document classes under `xlyEntity/.../mongo/`). Every `@Document` class is `PLAT_*`-named and every `MongoTemplate` caller lives in an `xlyPlat*` module — so MongoDB is part of the plat tier above. The framework layer this wiki covers is MySQL-only. ## How to fix something in this wiki diff --git a/en/docs/reference/maintainer/index.md b/en/docs/reference/maintainer/index.md index f506a57..92329c9 100644 --- a/en/docs/reference/maintainer/index.md +++ b/en/docs/reference/maintainer/index.md @@ -5,6 +5,8 @@ For Java developers maintaining or extending the framework itself. This chapter is for the people who will modify `BusinessBaseController`, add a new metadata table to the `gds*` family, or wire in a new third-party integration. +- [Running xlyEntry locally](running-locally.md) — first-run setup for developers (prereqs, profile, DB override, smoke check). +- [Tech stack](tech-stack.md) — library inventory by category (versions, where each is used, and why). - [The runtime: BusinessBaseController & friends](runtime.md) — the metadata-driven dispatch loop. - [Generic procedure dispatch](proc-dispatch.md) — `GenericProcedureCallController` deep dive. - [Cache invalidation on metadata change](cache-invalidation.md) — `ConsumerChangeGdsModuleThread` and friends. diff --git a/en/docs/reference/maintainer/running-locally.md b/en/docs/reference/maintainer/running-locally.md new file mode 100644 index 0000000..339768e --- /dev/null +++ b/en/docs/reference/maintainer/running-locally.md @@ -0,0 +1,87 @@ +# Running xlyEntry locally + +This page is the developer onboarding path: clone the repo, get +`xlyEntry` started on your machine, and hit a framework endpoint. The +companion [Multi-service deployment](deployment.md) page covers the +operator view (multiple services, profile permutations, nginx). + +## Prerequisites + +| Tool | Version notes | +|---|---| +| JDK 8 | `xly-src/build.gradle` pins `sourceCompatibility = 1.8`. JDK 11+ has not been validated. | +| Gradle wrapper | Bundled (`./gradlew`). Don't install Gradle separately. | +| MySQL 8 | Driver `com.mysql.cj.jdbc.Driver`. The schema in your DB must match what the active profile yaml points at — see "DB override" below. | +| Redis | The `local` profile expects `127.0.0.1:16379` with password `xlyXLY2015` (see `xlyEntry/src/main/resources/application-local.yml`). | +| ActiveMQ + MongoDB | The `local` profile points at `t0.xlyprint.com` for both. If you have network access to that host, you don't need to run them locally. | + +For the full library inventory the runtime brings up — Spring Boot +2.2.5, MyBatis 2.1.2, Druid, Activiti, Shiro, iText, Aspose, and the +rest — see [Tech stack](tech-stack.md). + +The auto-catalog generator (`scripts/gen_catalog.py`) is unrelated to +running xly — it only reads schema metadata via `~/.my.cnf`. See +[Contributing](../../contributing/index.md) for that path. + +## DB override + +The committed `xlyEntry/src/main/resources/application-local.yml` has +`spring.datasource.url` pointing at a remote dev DB. Most developers +override that to a local-or-personal target. Two options: + +1. **Edit the yaml** in your working tree. Don't commit the change. +2. **Override at the JVM level** with `-Dspring.datasource.url=...` + when invoking `bootRun`. + +The wiki's recon scripts use `~/.my.cnf` for `xlyweberp_saas_ai` — +that's a *different* schema than what `application-local.yml` ships +with (`xlyweberp_saas`). Pick one schema and stay consistent. + +## Run + +From `xly-src/`: + +```bash +./gradlew :xlyEntry:bootRun -Dspring.profiles.active=local +``` + +`xlyEntry` is packaged as a WAR (see its `build.gradle`) but Spring +Boot's embedded Tomcat is what `bootRun` uses, so no external Tomcat +deploy is needed for development. + +## Smoke check + +Once startup completes (look for `Tomcat started on port(s): 8080`): + +| URL | What you should see | +|---|---| +| `http://localhost:8080/xlyEntry/` | The framework landing page. Unauthenticated pages render; authenticated ones bounce to `/login`. | +| `http://localhost:8080/xlyEntry/druid/` | The Druid datasource monitor. Default credentials `xly` / `666666` are in the yaml — change them before exposing the port. | + +If the smoke check fails, look in the log directory configured at +`logging.dirpath` in the active profile yaml. + +## What `local` does and doesn't include + +The `local` profile boots **only** the framework runtime. It does not +boot: + +- `xlyApi` — start it as a second JVM if you need the external API surface. +- `xlyInterface` — same. +- `xlyPlc`, `xlyFlow`, `xlyFace` — same; each has its own application class and profile. +- The `xlyErpJms*` consumers — they are standalone Spring Boot apps. Without them, JMS-driven cache invalidation will not happen across nodes (fine for a single-node dev box). + +For multi-service local development, see +[Multi-service deployment](deployment.md). + +## When startup fails + +The two most common failure modes: + +1. **`Communications link failure`** — the `spring.datasource.url` + target is unreachable from your machine. Override it (see "DB + override"). +2. **`Connection refused: t0.xlyprint.com:61616` or `:27017`** — your + network can't reach the shared dev infra. You can either get on + the right network, point the yaml at local instances, or comment + out the relevant beans for the duration of your debugging session. diff --git a/en/docs/reference/maintainer/tech-stack.md b/en/docs/reference/maintainer/tech-stack.md new file mode 100644 index 0000000..a5d084b --- /dev/null +++ b/en/docs/reference/maintainer/tech-stack.md @@ -0,0 +1,232 @@ +# Tech stack + +A library inventory for the **in-scope** framework modules +(`xlyEntry`, `xlyApi`, `xlyManage`, `xlyBusinessService`, `xlyPersist`, +`xlyEntity`, `xlyFlow`, `xlyPlc`, `xlyInterface`, `xlyMsg`, +`xlyErpJms*`). + +The plat tier (`xlyPlat*` modules), `xlyFace`, and AI libraries are +[out of scope](../../index.md#whats-out-of-scope) and are not listed +here. + +## How to read this page + +Two columns carry evidence: + +- **Where** — the `build.gradle` files that declare the library as + `api(...)` or `implementation(...)`. Most libraries are declared in + `xlyPersist/build.gradle` and propagate transitively to the modules + that depend on `xlyPersist`. +- **In-scope source references** — file count from + `grep -rln xly-src//src/` across the in-scope + modules above, with a representative file path. A "no source + references found" entry means the library is declared but no Java, + HTML, or yaml source under any in-scope module references it + directly. Such libraries may still be loaded into the classpath as + transitive dependencies or consumed via Spring Boot autoconfig. + +What this page does not do: explain *why* a particular library was +chosen. Where the gradle file or yaml carries an explicit comment +(e.g., the Netty version pin), that comment is quoted. Otherwise the +page records facts only. + +## 1. Application platform + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Spring Boot | 2.2.5 | `xlyPersist/build.gradle` (web, starter, aop, data-redis, data-mongodb, websocket, activemq, freemarker), `xlyApi/build.gradle`, `xlyFlow/build.gradle` | All `*ApplicationBoot.java` classes (`xlyEntry`, `xlyApi`, `xlyInterface`, `xlyPlc`, `xlyFace`-when-built) extend `SpringBootServletInitializer`. | +| Embedded Tomcat | (Spring Boot 2.2.5 BOM) | transitively from `spring-boot-starter-web` | `xlyEntry/src/main/resources/application-local.yml` lines 10-29 configure `server.port: 8080`, `server.servlet.context-path: /xlyEntry`, `server.tomcat.*`. | +| Netty | 4.1.65.Final | `xlyPersist/build.gradle` | No direct Java imports. The gradle file comment states: "*引入mq后netty-common-4.1.45.Final.jar、与netty-all-4.0.42.Final.jar冲突,所以引入*" — the version is pinned to override conflicting transitive versions pulled in by MQ libraries. | +| AspectJ Weaver | 1.9.6 | `xlyApi/build.gradle` (declared twice) | 1 file: `xlyFlow/src/main/java/...` (one Java import). Spring Boot's AOP starter is the dominant consumer; the explicit pin in xlyApi is independent of imports. | +| Lombok | 1.18.8 (`xlyPersist`, `xlyFlow`) / 1.18.20 (`xlyApi`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 19 files import `lombok.*`. xlyApi declares it as both `implementation` and `annotationProcessor`. | + +## 2. Persistence + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| MyBatis | 2.1.2 (`mybatis-spring-boot-starter`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 102 Java files import `org.apache.ibatis.*` or `org.mybatis.*` — 76 in `xlyPersist`, the rest spread across xlyApi, xlyFlow, xlyInterface. Mapper XMLs live at `xlyPersist/src/main/resources/mapper/{erptable,business,test}/`. | +| MyBatis-Plus | 3.3.0 | `xlyApi/build.gradle` | 2 files: `xlyApi/src/main/java/com/xly/api/util/SqlUtil.java`, `xlyApi/src/main/java/com/xly/api/web/BaseController.java`. Not used outside xlyApi. | +| MySQL Connector/J | 8.0.13 | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | yaml `spring.datasource.driverClassName: com.mysql.cj.jdbc.Driver` (e.g., `xlyEntry/.../application-local.yml:127`). | +| MSSQL JDBC | sqljdbc4 3.0 (Maven) + `mssql-jdbc-6.2.2.jre8.jar` (local jar in `xlyFlow/`, `xlyInterface/`) | `xlyApi/build.gradle`, `xlyInterface/build.gradle`, `xlyFlow/build.gradle` | 5 files: 3 in `xlyFlow/src/`, 2 in `xlyInterface/src/`. | +| Oracle JDBC | `ojdbc6-11.2.0.4.jar` (local jar in `xlyFlow/`) | `xlyFlow/build.gradle` | 2 files in `xlyFlow/src/`. | +| Druid | `druid-spring-boot-starter` 1.2.16; `druid` 1.2.16 | `xlyPersist/build.gradle`, `xlyApi/build.gradle` | 25 files import `com.alibaba.druid.*` (xlyEntry=8, xlyPlc=8, xlyFlow=5, xlyBusinessService=2, xlyInterface=2). yaml: `xlyEntry/.../application-local.yml:126` sets `spring.datasource.type: com.alibaba.druid.pool.DruidDataSource`; lines 308-313 configure the `/druid/*` stat-view servlet. | +| HikariCP | 4.0.3 | `xlyApi/build.gradle` | 8 files reference `com.zaxxer.hikari` (xlyApi=6, xlyInterface=2). Java config: `xlyApi/.../api/config/MasterDataSourceConfig.java`, `SlaveDataSourceConfig.java`. yaml: `xlyApi/.../application-{local,dev,linux,win}.yml`. | +| Flyway | 5.2.1 | `xlyPersist/build.gradle` | No Java imports. Configured via yaml `spring.flyway.*` (e.g., `xlyEntry/.../application-local.yml:316-327`) with `enabled: false`. Migration scripts at `xlyEntry/src/main/resources/flyway/V*__*.sql`. | +| PageHelper | 4.1.1 | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 19 files import `com.github.pagehelper.*`. yaml `pagehelper.helperDialect: mysql` at `xlyEntry/.../application-local.yml:427`. | +| jsqlparser | 3.2 | `xlyPersist/build.gradle` | 1 file: `xlyPersist/src/...` imports `net.sf.jsqlparser`. | + +## 3. Cache & in-memory + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Spring Data Redis | 2.2.5 | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 3 Java files import `org.springframework.data.redis.*` (xlyEntry=1, xlyInterface=2). yaml: `spring.redis.*` blocks across all modules. | +| Lettuce | (Spring Data Redis 2.2.5 default driver) | transitive | No direct Java imports. Configured via yaml `spring.redis.lettuce.pool.*` (e.g., `xlyEntry/.../application-local.yml:385-394`). | +| Jedis | 2.9.0 | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 5 files import `redis.clients.jedis` (xlyPersist=2, xlyApi=2, xlyMsg=1, e.g. `xlyMsg/.../wechat/util/JedisMsgUtil.java`). | +| Guava | 18.0 (`xlyPersist`, `xlyApi`); 20.0 (`xlyFlow`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 8 files import `com.google.common.*`. | + +## 4. Workflow & scheduling + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Activiti Engine | 5.17.0 | `xlyPersist/build.gradle`, `xlyApi/build.gradle`; consumed by `xlyFlow` | 35 files import `org.activiti.*` (xlyFlow=32, plus 1 each in xlyPersist, xlyApi, xlyEntry — the xlyEntry hit is the `SecurityAutoConfiguration` exclusion in `EntryApplicationBoot.java`; the xlyApi hit is `IdGen.java`; the xlyPersist hit is `BaseDao.java`). The version skew with the 6.0 modeler libs is documented in [Activiti integration](activiti.md). | +| Activiti Spring Boot REST API | 6.0.0 | `xlyFlow/build.gradle` | Consumed via Spring Boot autoconfig + REST endpoints under `xlyFlow`. | +| Activiti JSON Converter | 6.0.0 | `xlyFlow/build.gradle` | (Used by xlyFlow's modeler save path.) | +| Quartz | 2.3.0 | `xlyFlow/build.gradle` | 16 files import `org.quartz.*` (xlyEntry=8, xlyFlow=8). yaml: `xlyEntry/.../application-local.yml:329-365` configures `spring.quartz.*` with JDBC JobStore (`qrtz_*` tables), `instanceName: xlyflowScheduler`. | + +## 5. Messaging + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Spring JMS + ActiveMQ starter | 2.2.5 | `xlyPersist/build.gradle` (`spring-boot-starter-activemq`) | 3 files import `org.springframework.jms.*` (xlyErpJmsProductor=2, xlyErpJmsConsumer=1); 2 files import `org.apache.activemq.*` (xlyErpJmsProductor=2). yaml: `spring.activemq.*` at `xlyEntry/.../application-local.yml:402-414`. See also [Messaging](../../api-reference/messaging.md). | +| RocketMQ Spring Boot Starter | 2.0.2 | `xlyPersist/build.gradle` | 4 files import `org.apache.rocketmq.*` in `xlyBusinessService/src/`. | + +## 6. View / templates + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Thymeleaf | 3.0.15 | `xlyApi/build.gradle`, `xlyFlow/build.gradle`; transitive via `spring-boot-starter-thymeleaf` | 2 Java files import `org.thymeleaf.*` (both in xlyFlow). Modeler templates in `xlyFlow/src/main/resources/templates/`. | +| Freemarker | 2.2.5 (Spring starter) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 1 Java file imports `freemarker.*` (xlyFlow). | +| Apache Batik | 1.8 (`xlyPersist`) / 1.7 (`xlyFlow`: codec, css, svg-dom, svggen) | `xlyPersist/build.gradle`, `xlyFlow/build.gradle` | 1 Java file imports `org.apache.batik.*` (xlyFlow). xlyFlow's modeler ships Batik static assets under `src/main/resources/static/modeler/editor-app/`. | + +## 7. Auth + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Apache Shiro (`shiro-spring`) | 1.3.2 (`xlyPersist`) / 1.4.2 (`xlyApi`, `xlyFlow`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 6 Java files import `org.apache.shiro.*` (xlyPersist=1, xlyApi=1, xlyFlow=4). The xlyPersist and xlyApi hits are both `IdGen.java` (crypto utilities); the xlyFlow hits are the modeler's auth helpers (`PermissionUtils`, `CacheUtils`, `IpUtils`, `ActExceptionHandler`). `EntryApplicationBoot.java` excludes Spring Security autoconfig. **No `@ConfigurationProperties("shiro")` or `shiro.*` property binding was found in any in-scope module**, despite a `shiro:` yaml block at `xlyEntry/.../application-local.yml:432-464`. The framework's HTTP auth pattern is documented in [API Reference cross-cutting facts](../../api-reference/index.md#cross-cutting-facts). | +| `shiro-ehcache` | 1.4.2 | `xlyFlow/build.gradle` | No direct Java imports under `xlyFlow/src/`. | +| `shiro-core` | 1.4.2 | `xlyFlow/build.gradle` | (Counted within the 6 `org.apache.shiro` hits above.) | +| `thymeleaf-extras-shiro` | 2.0.0 | `xlyFlow/build.gradle` | 47 `.html` template files reference Shiro tags (xlyApi=5, xlyFlow=42). No Java imports. | +| Bouncy Castle | `bcprov-jdk14:138` | `xlyApi/build.gradle` | 2 files: `xlyApi/src/main/java/com/xly/api/util/RsaEncrypt.java`, `xlyInterface/src/main/java/com/xly/util/RsaEncrypt.java`. | +| commons-codec | 1.16.0 | `xlyPersist/build.gradle`, `xlyApi/build.gradle` | 18 files import `org.apache.commons.codec.*` (xlyApi=6, xlyInterface=6, xlyMsg=4, xlyPersist=2). | + +## 8. Reporting & export + +The framework's print/export surface is the largest single consumer of +third-party code. + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| iText (5.x branch) | itextpdf 5.5.0 + itext-pdfa 5.5.0 + itext-asian 5.2.0 | `xlyPersist/build.gradle` | 1 file: `xlyPersist/src/main/java/com/xly/jxls/Util/PdfConverUtil.java` imports `com.itextpdf.*`. | +| iText (legacy 2.x via lowagie) | `com.lowagie:itext` 2.1.7 | `xlyPersist/build.gradle` | 1 file in `xlyPersist/src/` imports `com.lowagie.text.*`. Both iText branches coexist on classpath. | +| Aspose Cells | `aspose-cells-21.8.cracked.jar` (local jar) | `xlyPersist/build.gradle` (`api files(...)`) | 6 files in `xlyPersist/src/` import `com.aspose.cells.*`. | +| Aspose Words | `aspose-words-15.8.0-jdk16.jar` (local jar) | `xlyPersist/build.gradle` | 1 file in `xlyPersist/src/` imports `com.aspose.words.*`. | +| Apache POI | 4.1.2 (`xlyPersist`, `xlyFlow`) / 3.15 (`xlyApi`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 36 files import `org.apache.poi.*` (xlyPersist=19, xlyFlow=6, xlyEntry=5, xlyBusinessService=5, xlyApi=1). | +| jxls + jxls-poi | 2.8.1 | `xlyPersist/build.gradle` | 22 files import `org.jxls.*` (xlyPersist=17, xlyEntry=5). | +| jxls-jexcel | 1.0.9 | `xlyPersist/build.gradle` | (Counted within jxls hits above.) | +| commons-jexl3 | 3.1 | `xlyPersist/build.gradle` | (Pulled in by jxls; no direct imports detected.) | +| EasyExcel | 4.0.3 (local jars: `easyexcel-4.0.3.jar` + `-support-4.0.3.jar` + `-core-4.0.3.jar`) | `xlyPersist/build.gradle` | 10 files in `xlyBusinessService/src/` import `com.alibaba.excel.*`. | +| JasperReports | `jasperreports-6.0.0.jar` + `jasperreports-fonts-6.0.0.jar` (local jars) | `xlyPersist/build.gradle` | 11 files import `net.sf.jasperreports.*` (xlyPersist=5, xlyEntry=3, xlyBusinessService=3). | +| OLAP4J | `olap4j-1.2.0.jar` + `olap4j-xmlaserver-1.2.0.jar` (local jars) | `xlyPersist/build.gradle` | 1 file in `xlyPersist/src/` imports `org.olap4j.*`. | +| ZXing | core 3.4.0 + javase 3.4.0 | `xlyPersist/build.gradle` | 5 files import `com.google.zxing.*` (xlyPersist=4, xlyEntry=1). | +| Barcode4J | `barcode4j-light` 2.0 | `xlyPersist/build.gradle` | 1 file in `xlyPersist/src/` imports `org.krysalis.barcode4j.*`. | +| Pinyin4j | 2.5.0 | `xlyPersist/build.gradle` | 1 file: `xlyPersist/src/main/java/com/xly/utils/ChineseCharacterUtil.java`. | +| PDFBox | 2.0.6 | `xlyPersist/build.gradle` | 2 files import `org.apache.pdfbox.*` (xlyPersist=1, xlyBusinessService=1). | +| Thumbnailator | 0.4.8 (`net.coobird`) | `xlyPersist/build.gradle` | 2 files import `net.coobird.*` (xlyPersist=1, xlyEntry=1). | +| jacob | `jacob.jar` (local jar) | `xlyPersist/build.gradle` | 2 files in `xlyPersist/src/` import `com.jacob.*`. | + +## 9. File storage & HTTP clients + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Aliyun OSS SDK | `aliyun-sdk-oss` 2.2.0 | `xlyPersist/build.gradle` | 1 file: `xlyPersist/src/main/java/com/xly/utils/OssUtil.java`. | +| commons-fileupload | 1.5 | `xlyFlow/build.gradle` | 1 file in `xlyFlow/src/` imports `org.apache.commons.fileupload.*`. | +| commons-io | 2.5 | `xlyFlow/build.gradle` | 7 files import `org.apache.commons.io.*` (xlyFlow=3, xlyPersist=2, xlyEntry=1, xlyBusinessService=1). | +| OkHttp + Okio | 4.10.0 / 2.10.0 | `xlyPersist/build.gradle` | 2 files in `xlyApi/src/` import `okhttp3.*`. | +| Apache HttpClient | 4.5.5 | `xlyPersist/build.gradle` | 1 file in `xlyBusinessService/src/` imports `org.apache.http.*`. | +| javax.mail | 1.6.2 | `xlyPersist/build.gradle` | 1 file in `xlyPersist/src/` imports `javax.mail.*`. | + +## 10. JSON & general utilities + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| FastJson | 1.2.15 (`xlyPersist`, `xlyApi`) / 1.2.60 (`xlyFlow`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 84 files import `com.alibaba.fastjson.*` across all in-scope modules (xlyBusinessService=39, xlyEntry=11, xlyInterface=10, xlyPersist=9, xlyFlow=6, xlyMsg=5, xlyApi=4). | +| Jackson | `jackson-databind` 2.9.7 (`xlyFlow` explicit) + transitive via Spring | `xlyFlow/build.gradle` | 22 files import `com.fasterxml.jackson.*` (xlyFlow=8, xlyInterface=9, xlyApi=2, xlyPersist=2, xlyEntry=1). | +| Hutool | `hutool-all` 5.6.5 (`xlyPersist`) / 5.8.5 (`xlyApi`, `xlyFlow`) | `xlyPersist/build.gradle`, `xlyApi/build.gradle`, `xlyFlow/build.gradle` | 271 files import `cn.hutool.*` across every in-scope module (xlyBusinessService=93, xlyFlow=47, xlyApi=37, xlyPersist=33, xlyEntry=25, xlyInterface=23, xlyMsg=10, xlyManage=2, xlyPlc=1). | +| commons-lang3 | 3.6 (`xlyPersist`) / 3.8.1 (`xlyFlow`) | `xlyPersist/build.gradle`, `xlyFlow/build.gradle` | 41 files import `org.apache.commons.lang3.*` (xlyFlow=24, xlyPersist=8, xlyEntry=3, xlyApi=2, xlyBusinessService=3, xlyMsg=1). | +| commons-collections4 | 4.1 | `xlyPersist/build.gradle` | 1 file in `xlyBusinessService/src/`. | +| Groovy | `groovy-all` 3.0.2 | `xlyPersist/build.gradle`, `xlyApi/build.gradle` | 5 Java files import `groovy.util.logging.Slf4j` (xlyPersist=3, xlyApi=2). The annotation is from Groovy's runtime; the Java files using it appear to be vestigial — the import is present but the annotation does not affect Java compilation. | +| Struts2 JSON plugin | 2.5.30 | `xlyPersist/build.gradle` | 1 file: `xlyPersist/src/main/java/com/xly/utils/FeedPage.java`. The framework otherwise runs on Spring MVC. | +| SnakeYAML | 1.27 | `xlyPersist/build.gradle`, `xlyFlow/build.gradle` | 1 file in `xlyFlow/src/` imports `org.yaml.snakeyaml.*`. | +| JDOM | 1.1 | `xlyApi/build.gradle` | 3 files import `org.jdom.*` (xlyApi=1, xlyInterface=1, xlyMsg=1). | +| validation-api | 2.0.1.Final | `xlyFlow/build.gradle` | 2 files in `xlyFlow/src/` import `javax.validation.*`. | + +## 11. Hardware integration + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| HslCommunication | `HslCommunication.jar` (local jar; no version metadata in filename) | `xlyPersist/build.gradle` | 9 files reference `HslCommunication` (xlyPersist=3, xlyBusinessService=3, xlyPlc=3). xlyPlc is the PLC bridge — see [Slice 6](../../slices/06-hardware.md). | + +## 12. Notifications + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Aliyun DingTalk SDK | `com.aliyun:dingtalk` 2.1.14 | `xlyMsg/build.gradle` | 1 file in `xlyMsg/src/main/java/com/xly/dingtalk/`. See [Notifications](../../api-reference/notifications.md). | +| `alibaba-dingtalk-service-sdk` | 2.0.0 | `xlyMsg/build.gradle` | 1 file in `xlyMsg/src/` imports `com.dingtalk.api.*`. | +| Jeewx-API (WeChat) | `jeewx-api-1.3.2.jar` (local jar) | `xlyInterface/build.gradle` | 5 files in `xlyInterface/src/` reference Jeewx packages. | + +## 13. Licensing + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| TrueLicense | `trueswing.jar` + `truexml.jar` + `turelicense.jar` (local jars) | `xlyBusinessService/build.gradle` | 5 files in `xlyBusinessService/src/main/java/com/xly/license/`: `LicenseManager.java`, `LicenseManagerHolder.java`, `VerifyLicense.java`, `CustomKeyStoreParam.java`, `Resources.java`. yaml: `License:` block at `xlyEntry/.../application-local.yml:80-87` (`checkLic: false` in local). | + +## 14. Logging + +| Library | Version | Where | In-scope source references | +|---|---|---|---| +| Logback | `logback-classic` 1.2.3 | `xlyPersist/build.gradle` | 5 files import `ch.qos.logback.*` across modules. Configuration: `xlyEntry/src/main/resources/logback-spring.xml`. | +| log4j (1.x) | 1.2.17 | `xlyPersist/build.gradle` | 1 file in `xlyFlow/src/` imports `org.apache.log4j.*`. yaml: Druid stat filter `filters: stat,log4j2` at `xlyEntry/.../application-local.yml:282`. | + +## 15. Build & dev + +| Library | Version | Where | What | +|---|---|---|---| +| Gradle wrapper | committed | repo root (`gradlew`, `gradle/`) | Build tool. See [Running locally](running-locally.md). | +| Spring Boot Gradle plugin | 2.2.5.RELEASE | repo-root `build.gradle` | Builds runnable WARs. | +| Spring Boot configuration processor | 2.2.5.RELEASE | `xlyApi/build.gradle` (`annotationProcessor`) | Generates IDE metadata for `@ConfigurationProperties` classes in xlyApi. | + +## Declared but no in-scope source references found + +The following libraries appear in `build.gradle` files but no Java +imports, HTML template references, or yaml property bindings were +found under `xly-src//src/`. They may exist on the +classpath as required transitive dependencies, may be consumed via +Spring Boot autoconfiguration, may have been used by code that has +since been removed, or may be vestigial. + +| Library | Declared in | Notes | +|---|---|---| +| Kaptcha (`kaptcha` 2.3.2) | `xlyFlow/build.gradle` | No imports found. Captcha JPEG generator. | +| JNA + jna-platform 4.5.2 | `xlyFlow/build.gradle` | No imports found. Native code access library. | +| oshi-core 3.9.1 | `xlyFlow/build.gradle` | No imports found. (oshi-core depends on JNA.) | +| UserAgentUtils 1.19 | `xlyFlow/build.gradle` | No imports found. | +| Barbecue `1.5-beta1` | `xlyPersist/build.gradle` | No imports found. (Alternate barcode library; Barcode4J + ZXing are the active paths.) | +| Gson 2.8.6 | `xlyPersist/build.gradle` | No `com.google.gson` imports found. FastJson and Jackson are the active JSON paths. | +| commons-pool2 2.8.0 | `xlyPersist/build.gradle` | No direct imports. Likely transitive support for Jedis or similar. | +| Baidu SDK (`baidu-sdk-1.4.5.jar`, local) | `xlyInterface/build.gradle` | No `com.baidu` imports found. | +| `mchange-commons-java` 0.2.11 | `xlyFlow/build.gradle` | No direct imports. | + +## Notable version skew & local jars + +Pulled directly from the `build.gradle` files. Each is a fact, not a recommendation. + +| Item | Detail | +|---|---| +| Shiro | 1.3.2 in `xlyPersist`; 1.4.2 in `xlyApi` and `xlyFlow`. | +| FastJson | 1.2.15 in `xlyPersist` and `xlyApi`; 1.2.60 in `xlyFlow`. | +| Hutool | 5.6.5 in `xlyPersist`; 5.8.5 in `xlyApi` and `xlyFlow`. | +| Apache POI | 4.1.2 in `xlyPersist` and `xlyFlow`; 3.15 in `xlyApi`. | +| Guava | 18.0 in `xlyPersist` and `xlyApi`; 20.0 in `xlyFlow`. | +| commons-lang3 | 3.6 in `xlyPersist`; 3.8.1 in `xlyFlow`. | +| Lombok | 1.18.8 in `xlyPersist` and `xlyFlow`; 1.18.20 in `xlyApi`. | +| iText | `itextpdf` 5.5.0 *and* `com.lowagie:itext` 2.1.7 are both declared in `xlyPersist`. | +| Activiti | engine 5.17.0 (declared in `xlyPersist` and `xlyApi`); rest-api and json-converter 6.0.0 (`xlyFlow`) — see [Activiti integration](activiti.md). | +| Local jars (not from Maven) | `xlyPersist/src/main/java/lib/`: `aspose-cells-21.8.cracked.jar`, `aspose-words-15.8.0-jdk16.jar`, `jacob.jar`, `HslCommunication.jar`, `QRCode.jar`, `olap4j-1.2.0.jar`, `olap4j-xmlaserver-1.2.0.jar`, `jasperreports-6.0.0.jar`, `jasperreports-fonts-6.0.0.jar`, `easyexcel-{,-support-,-core-}4.0.3.jar`. `xlyFlow/src/main/java/lib/`: `mssql-jdbc-6.2.2.jre8.jar`, `ojdbc6-11.2.0.4.jar`. `xlyInterface/src/main/java/lib/`: `mssql-jdbc-6.2.2.jre8.jar`, `baidu-sdk-1.4.5.jar`, `jeewx-api-1.3.2.jar`. `xlyBusinessService/src/main/java/lib/`: `trueswing.jar`, `truexml.jar`, `turelicense.jar`. | +| Spring Boot | 2.2.5.RELEASE — pinned in the root `build.gradle` plugin block and declared at every module that uses Spring Boot starters. | + +## What's intentionally not in this list + +- The plat tier (`xlyPlat*` modules) and dependencies declared only there — out of scope per [index](../../index.md#whats-out-of-scope). +- AI / LLM libraries (`com.theokanning.openai-gpt3-java:service` 0.11.1 and `com.unfbx:chatgpt-java` 1.0.8 in `xlyApi/build.gradle`) — out of scope. +- The MongoDB starter declared in `xlyEntity/build.gradle` (`spring-boot-starter-data-mongodb` 2.2.5). The `xlyEntity` module contains 22 `@Document`-annotated classes under `xlyentity/mongo/`, all named `PLAT_*`. A grep for `MongoTemplate` and `MongoRepository` in in-scope modules returned only `xlyPersist/.../dao/platmongo/BaseMongoDao.java` (which serves the plat tier); no in-scope module invokes Mongo APIs. See the [out-of-scope note in index](../../index.md#whats-out-of-scope). +- `xlyFace` — out of scope. diff --git a/en/mkdocs.yml b/en/mkdocs.yml index d5fe0d6..4606c7f 100644 --- a/en/mkdocs.yml +++ b/en/mkdocs.yml @@ -101,6 +101,8 @@ nav: - "How to set permissions": reference/builder/permissions.md - 4. Reference (Maintainer): - reference/maintainer/index.md + - "Running xlyEntry locally": reference/maintainer/running-locally.md + - "Tech stack": reference/maintainer/tech-stack.md - "The runtime: BusinessBaseController & friends": reference/maintainer/runtime.md - "Generic procedure dispatch": reference/maintainer/proc-dispatch.md - "Cache invalidation on metadata change": reference/maintainer/cache-invalidation.md @@ -113,6 +115,7 @@ nav: - "External API (xlyApi)": api-reference/external.md - "Inbound webhooks (xlyInterface)": api-reference/webhooks.md - "Messaging (ActiveMQ / RocketMQ)": api-reference/messaging.md + - "Notifications (xlyMsg)": api-reference/notifications.md - 6. Auto-Catalog: - auto-catalog/index.md - Tables: auto-catalog/tables/index.md diff --git a/zh/docs/api-reference/external.md b/zh/docs/api-reference/external.md new file mode 100644 index 0000000..970bc89 --- /dev/null +++ b/zh/docs/api-reference/external.md @@ -0,0 +1,105 @@ +# 外部 API(`xlyApi`) + +`xlyApi` 是外部集成方使用的 API 接口面。它作为独立的 Spring Boot WAR 运行(入口类 `xlyApi/src/main/java/com/xly/ApiApplicationBoot.java`),context-path 为 `/xlyApi`,并拥有自己的 `application-*.yml` 文件。 + +它最关键的设计选择是:**大多数外部端点不是硬编码在 Java 里的,而是 `sysapi` 中的数据行**。新的外部 API 和 xly 其他部分一样,是“注册成数据”的。运行时负责查找 `sApiCode`、验证调用方,并执行元数据指定的内容。 + +## 数据驱动分发器:`/api/invoke/{sApiCode}` + +最重要的单个端点: + +```http +POST /xlyApi/api/invoke/{sApiCode} +Authorization: Bearer (or query param ?authorizationt=) +Content-Type: application/json +{ ...request body, passed through as `sBody` to the handler... } +``` + +处理器:`xlyApi/src/main/java/com/xly/api/web/ApiController.java` 中的 `ApiController.invoke()`。 + +流程: + +1. 读取 `Authorization` header(回退:`authorizationt` 查询参数)。 +2. 查找以 `sApiCode` 为键的 `sysapi` 行(通过 `ApiServiceImpl.invoke` → `SELECT … FROM sysapi WHERE sApiCode = #{sApiCode}`)。 +3. 如果该行的 `bHasToken` 标志已设置,就用 `sysapithirdtoken`(或该行指向的等价 token 存储)验证 token。 +4. 使用请求 body 合并出的参数 map,执行 `sysapi.sDataSql` 中存放的 SQL 模板。 +5. 将调用写入 `sysapilog`。 +6. 用 `AjaxResult` 包装结果并返回。 + +因此,新增一个外部 API 是管理 / PM 任务:向 `sysapi` 插入一行,提供 SQL 模板,设置标志,把 `sApiCode` 和 token 给集成方。不需要改 Java 代码。 + +### 需要关注的 `sysapi` 列 + +| 列 | 含义 | +|---|---| +| `sApiCode` | 消费方发送的 path variable。每个租户内必须唯一。 | +| `sApiName` | 人类可读标签。 | +| `sApiUrl` / `sApiUrlRef` | 计算后的 URL,即 `sApiUrlRef + sApiCode`,用于出站分发。 | +| `sMethod` | 此 API 期望的 HTTP 方法(`GET`、`POST` 等)。 | +| `sHeader`、`sBody`、`sBodyNode`、`sBodyType` | 描述入站请求形状的模板。 | +| `sDataSql` | 运行时执行的 SQL 模板。`#{xxx}` 引用会由请求参数填充。 | +| `sDataTest`、`sDataTestNode` | BACK API 测试器(`POST /api/apiTest`)使用的请求 / 响应样例。 | +| `bHasToken` | `1` 表示要求 `Authorization` 中携带 token;`0` 表示公开。 | + +## API 管理端点 + +`/api/list/...`、`/api/data/...`、`/api/add`、`/api/edit`、`/api/remove` 是管理 `sysapi` 行本身的后台接口面(BACK 的“自定义接口”页面会调用这些)。`POST /api/getSqlTemple` 为给定 API 名称生成 SQL 骨架;`POST /api/apiTest` 使用 `sDataTest` 中的测试夹具跑一次端到端 dry-run。 + +## Token 端点:`/token/*` + +| Endpoint | Method | 用途 | +|---|---|---| +| `/token/getToken?corpid=&corpsecret=` | POST | 为集成方的 `(corpid, corpsecret)` 组合签发 bearer token。 | + +返回的 token 就是 `/api/invoke/{sApiCode}` 期望在 `Authorization` 中收到的值。完整实现位于 `xlyApi/src/main/java/com/xly/api/web/TokenController.java` 及其 service。 + +对于 xly 自己获取的**第三方** token(为了代表客户调用集群外 API),见 `/thirdtoken/*`,它由 `sysapithirdtoken` 支撑(表注释:第三方token获取接口)。 + +## 其他收敛后的接口面 + +这些是同一个 WAR 中托管的较小专用 API: + +| Endpoint root | Controller | 用途 | +|---|---|---| +| `/online/api/{sApiCode}` | `OnlineController` | 只读执行某个 `sysapi` 行(不写入)。适合公开数据端点。 | +| `/online/onlineword/{sApiCode}` | `OnlineController` | 返回“word”风格模板载荷的变体。 | +| `/online/onlinelist`, `/online/getToken` | `OnlineController` | 在线 API 列表及其 token 签发。 | +| `/pro/get/{sProName}` | `ProContentController` | 按名称获取存储过程元数据。 | +| `/pro/getData/{sProName}` | `ProContentController` | 执行指定存储过程并返回其结果集。 | +| `/pro/alert/{redisId}`, `/pro/getAlertValue/{redisId}` | `ProContentController` | 按 Redis id 读取预警 / 通知值。 | +| `/pro/executeSql` | `ProContentController` | 执行参数化 SQL 模板(管理级)。 | +| `/thirdparty/*` | `ThirdPartyController` | 第三方 API 定义 CRUD,以及 `checkPartyApi` 校验器。由 `sysapithirdparty` 支撑。 | +| `/thirdtoken/*` | `ThirdTokenController` | 出站 token 配置 CRUD。由 `sysapithirdtoken` 支撑。 | +| `/brand/*` | `BrandController` | 伙伴 / 供应商列表(`sysapibrand`)CRUD。 | +| `/json/*`, `/json/jsonEdit/{sDomId}` | `JsonController` | BACK API 编辑器使用的 JSON 树编辑辅助端点。 | +| `/interfaceDefine/callthirdparty/{interfaceInvoke}` | `InterfaceController` | 分发由 `sysapithirdparty` 定义的出站调用。 | +| `/gpt/chatGpt` | `GptController` | 实验性 GPT/chat 接口的透传;不属于框架 Wiki 范围。 | + +## 支撑表(数据库层接口面) + +这些表保存 API 元数据。所有表都带有 `sBrandsId` / `sSubsidiaryId`,用于租户作用域。 + +| 表 | 角色 | +|---|---| +| `sysapi` | `/api/invoke` 使用的 API 定义。 | +| `sysapilog` | 每次调用的审计日志。 | +| `sysapibrand` | 伙伴 / 供应商目录。 | +| `sysapithirdparty` | 出站第三方端点定义。 | +| `sysapithirdtoken` | 出站第三方 token 配置。 | +| `sysapidbtodb` | DB-to-DB 同步 API 定义。 | +| `sysapidbtodblog` | DB-to-DB 同步运行日志。 | + +## 集成方如何使用 + +典型的首次集成: + +1. **让管理员注册你的 API。** 向 `sysapi` 插入一行(通过 BACK 自助页面或直接写库):选择 `sApiCode`,编写 `sDataSql`,如果需要 token 认证则设置 `bHasToken = 1`,保存。 +2. **获取 token。** `POST /xlyApi/token/getToken?corpid=&corpsecret=`。 +3. **调用 API。** 使用 `Authorization` 中的 token 和 JSON body 调用 `POST /xlyApi/api/invoke/`。 +4. **检查日志。** `sysapilog` 会记录每次调用、响应 `iStatus` 和 body。BACK 管理页面也会展示同样的信息。 + +## 这个 API 不是什么 + +- **不是 OpenAPI 描述的接口。** 任意 `sApiCode` 的契约由其 `sysapi.sDataSql` 和 `sBody` 行决定。规格要向注册该 API 的团队获取,而不是从这里的 `/swagger` 端点获取。 +- **不是 `xlyEntry` 内部 API 的薄封装。** 这些是有意分开的接口面。不支持从外部调用 `xlyEntry` 上的 `/business/*`。 +- **不是入站 webhook 接收器。** 第三方推送事件走 [`xlyInterface`](webhooks.md)。 diff --git a/zh/docs/api-reference/index.md b/zh/docs/api-reference/index.md new file mode 100644 index 0000000..0927053 --- /dev/null +++ b/zh/docs/api-reference/index.md @@ -0,0 +1,21 @@ +# 5. API 参考 + +xly 暴露了三个不同的 HTTP 接口面,分别由三个独立的 Spring Boot 服务承载。每个接口面都在下面的独立页面中说明;概念总览见[概念 / 三层 API](../concepts/api-surface.md)。 + +| 页面 | 服务 | Context path | 适用场景 | +|---|---|---|---| +| [内部 API](internal.md) | `xlyEntry` | `/xlyEntry` | 你在扩展 SPA,或维护框架运行时。 | +| [外部 API](external.md) | `xlyApi` | `/xlyApi` | 你在接入会从外部调用 xly 的系统。 | +| [Webhook](webhooks.md) | `xlyInterface` | `/xlyInterface` | 第三方系统需要把事件推送进 xly。 | +| [消息](messaging.md) | `xlyEntry` + `xlyErpJms*` | 不适用(ActiveMQ / RocketMQ) | 异步、扇出式集成比同步 HTTP 调用更合适。 | +| [通知](notifications.md) | `xlyMsg`(作为库被 `xlyEntry`、`xlyBusinessService`、`xlyInterface` 使用) | 不适用(钉钉 / 微信 API) | 业务事件需要向用户推送聊天平台消息。 | + +## 阅读顺序 + +如果你是外部集成方,从[外部 API](external.md) 开始。如果你是扩展框架的 Java 开发者,从[内部 API](internal.md) 开始(通用 CRUD 机制的大部分内容在[维护人员运行时章节](../reference/maintainer/runtime.md)中说明;本章会交叉链接到那里)。 + +## 跨接口面的共同事实 + +- **认证。** 内部调用使用 SPA 的 session cookie + `@CurrentUser` 参数解析器。外部调用使用从 `/xlyApi/token/getToken` 获取的 bearer token。Webhook 按通道约定认证方式(签名 header、查询参数密钥等)。 +- **租户作用域。** 每个服务中,所有已认证控制器方法在做任何工作前都会执行 `RequestAddParamUtil.me().addParams(params, userInfo)`;见[多租户概念页](../concepts/multi-tenancy.md)。绕过这一步的请求就是多租户 bug。 +- **日志。** 外部调用写入 `sysapilog`;内部调用使用框架标准的 `syslog4j` 配置;Webhook 由各通道处理器自行决定日志方式。 diff --git a/zh/docs/api-reference/internal.md b/zh/docs/api-reference/internal.md new file mode 100644 index 0000000..4493b6f --- /dev/null +++ b/zh/docs/api-reference/internal.md @@ -0,0 +1,62 @@ +# 内部 API(`xlyEntry`) + +`xlyEntry` 服务承载 SPA 的运行时 API。它是三层中最大的一层:控制器和框架的元数据驱动运行时编译到同一个 WAR,大多数调用会落到少数几个可以读写任意模块的通用端点上。 + +这个 API **不是给外部调用方使用的稳定契约**。端点形状会随框架变化而变化。外部集成应该使用[外部 API](external.md)。本页面面向维护人员和 SPA 扩展作者。 + +请求生命周期和代码级 walkthrough 见[维护人员运行时章节](../reference/maintainer/runtime.md)。本页只列 HTTP 入口。 + +## 通用 CRUD 接口面:`/business/*` + +| Endpoint | Method | 用途 | +|---|---|---| +| `/business/getModelBysId/{moduleId}` | GET | 返回某个模块的表单布局,也就是五键组合(`formData`、`gdsformconst`、`gdsjurisdiction`、`billnosetting`、`report`)。 | +| `/business/getBusinessDataByFormcustomId/{formId}` | POST | 返回某个表单的业务数据行,带分页。设置 `sGroupList` 时会分支到 `getBusinessDataByGroup`。 | +| `/business/getBusinessDataByIndex` | POST | 首条 / 末条 / 下一条 / 上一条记录导航。 | +| `/business/addBusinessData` | POST | 单行新增。 | +| `/business/addUpdateDelBusinessData` | POST | 在一个事务调用里组合新增 + 修改 + 删除。前端通过 `sTable` 直接指定目标表。 | +| `/business/getSelectDataBysControlId/{sId}` | POST | 按控件 `sId` 为单个控件加载下拉选项。 | +| `/business/getSelectLimit/{sId}` | POST | 下拉加载调用的分页变体。 | + +这些端点在[切片 1](../slices/01-hello-world.md)(`getModelBysId` + 网格加载 + 保存)和[切片 3](../slices/03-report.md)(基于视图的读取变体)中有更详细说明。处理类位于 `xlyEntry/src/main/java/com/xly/web/businessweb/`。 + +## 元数据管理端点 + +配置侧动作(创建模块、定义表单、声明虚拟表)在 `xlyEntry/src/main/java/com/xly/web/systemweb/` 下有一套并行接口面: + +| Endpoint root | Controller | 用途 | +|---|---|---| +| `/gdsmodule/*` | `GdsmoduleController` | 模块树 CRUD,包括 `getModuleTreePro`、`addGdsmodule`、`updateGdsmodule`。 | +| `/gdsconfigform/*` | `GdsconfigformController` | 表单主表和表单明细元数据 CRUD。 | +| `/gdsconfigtb/*` | `GdsconfigtbController` | 虚拟表主表 / 明细元数据 CRUD。 | + +## 专用运行时端点 + +| Endpoint root | Controller | 用途 | +|---|---|---| +| `/configform/*` | `BusinessConfigformController` | 用户 / 用户组级显示定制。 | +| `/treegrid/*` | `BusinessTreeGridController` | 树表端点(当前分支实现的是存储过程支撑路径)。 | +| `/procedureCall/*` | `GenericProcedureCallController` | 按名称 + 参数通用调用存储过程;见[通用存储过程分发](../reference/maintainer/proc-dispatch.md)。 | +| `/panel/*` | `ConfigformPanelController` | `gdsconfigformpanel` 中的面板布局持久化。 | +| `/checkFlow/*` | `CheckFlowController` | Activiti 工作流接口面(审批 / 驳回 / 查看),只在运行流程引擎的部署中有意义。 | + +## 报表与打印 + +打印接口面位于 `xlyEntry/src/main/java/com/xly/web/report/`: + +- `PrintReportController` — 当前 jxls / iText 打印路径。 +- `PrintReportControllerOld` — 为旧模板保留的历史打印路径。 + +前端的“打印” / “导出”按钮会调用这些控制器,控制器从 `sysreport` 加载模板,执行匹配的视图查询,并把二进制文件流回前端。流程见[切片 3](../slices/03-report.md)。 + +## 认证 + +所有参与业务数据的控制器方法都标注 `@Authorization`,并通过 `@CurrentUser` 接收解析后的 `UserInfo`。session 到 `UserInfo` 的映射由框架自己处理(cookie + Redis 支撑的 session);见[多租户概念页](../concepts/multi-tenancy.md)。 + +如果某个请求未认证却进入了控制器,正常情况下会被 `@Authorization` 拦下;如果没有被拦下(例如某个方法未加注解),这个方法也会绕过 `RequestAddParamUtil` 中的通用租户注入,因此就是多租户 bug。 + +## 这个 API 不是什么 + +- **不是稳定接口。** 端点形状会随框架变化。 +- **不是给外部调用方认证的接口。** 这里没有 API key 流程;cookie/session 不是集成方需要的东西。 +- **不是自助式公开文档。** 这个接口面太大、太通用了,不适合发布成 OpenAPI 文档。外部集成方应该使用经过收敛的[外部 API](external.md)。 diff --git a/zh/docs/api-reference/messaging.md b/zh/docs/api-reference/messaging.md new file mode 100644 index 0000000..39e05b5 --- /dev/null +++ b/zh/docs/api-reference/messaging.md @@ -0,0 +1,38 @@ +# 消息(ActiveMQ / RocketMQ) + +xly 中不是所有集成都适合同步 HTTP 调用。框架运行两个消息代理,它们职责不同: + +| Broker | 用途 | Producer | Consumer | +|---|---|---|---| +| **ActiveMQ / JMS** | 缓存失效、集群内扇出事件。元数据变更链路([缓存失效](../reference/maintainer/cache-invalidation.md))依赖它。 | `xlyErpJmsProductor` | `xlyErpJmsConsumer` | +| **RocketMQ** | 其他不适合 ActiveMQ 假设的集成流程。 | `RocketMQServiceImpl`(位于 `xlyBusinessService`) | 因服务而异。 | + +本页只是指针而不是深入说明;准确的队列名和载荷在 `xlyErpJmsConsumer/src/main/java/com/xly/xlyerpjmsconsumer/` 下按 consumer thread 级别记录。 + +## ActiveMQ / JMS:缓存失效通道 + +Producer 侧队列声明位于 `xlyErpJmsProductor/src/main/java/com/xly/xlyerpjmsproductor/config/P2pQueue.java`。当前框架使用的主要 destination(完整列表请读该文件): + +| Constant | 用途 | +|---|---| +| `ERP_JMS_ACTIVEMQ_CHANGE_GDS_MODULE` | “模块元数据已变更”,触发 `ConsumerChangeGdsModuleThread` 清理各节点相关 Redis 缓存。见[元数据变更后的缓存失效](../reference/maintainer/cache-invalidation.md)。 | +| `ERP_JMS_ACTIVEMQ_CHANGE_ELE_CUSTOMER` | 客户主数据变更扇出。 | +| `ERP_JMS_ACTIVEMQ_CHANGE_ELE_EMPLOYEE` | 员工主数据变更扇出。 | +| `ERP_JMS_ACTIVEMQ_CHANGE_ELE_MACHINE` | 车间机台主数据变更扇出。 | +| `ERP_JMS_ACTIVEMQ_UPD_SALE_ORDER`、`ERP_JMS_ACTIVEMQ_UPD_WORK_ORDER`、`ERP_JMS_ACTIVEMQ_UPD_PRODUCTION_REPORT` | “单据已更新”通知,由后台 worker 消费(合计重算、下游失效等)。 | +| `ERP_JMS_ACTIVEMQ_DEL_SALE_ORDER`、`ERP_JMS_ACTIVEMQ_DEL_WORK_ORDER`、`ERP_JMS_ACTIVEMQ_DEL_PRODUCTION_REPORT` | 单据删除通知。 | + +每个 destination 在 `xlyErpJmsConsumer/.../thread/` 下都有对应的 `Consumer*Thread` 类异步处理消息。 + +## RocketMQ:其他流程 + +`RocketMQServiceImpl` 及其配套 `RocketMQService` 接口位于 `xlyBusinessService/src/main/java/com/xly/service/`。它们覆盖非缓存失效流程(例如 `MQCompensateServiceImpl` 处理 ActiveMQ destination 难以表达的补偿 / 重试语义)。RocketMQ topic 按环境配置。 + +## 手动触发缓存失效 + +如果元数据变更是通过原始 SQL 完成的(没有 JMS 事件),各节点缓存不会自动清理。支持的覆盖路径是 `xlyBusinessService/.../service/impl/` 中的 `BusinessCleanRedisDataImpl`,它可以直接发布失效事件。更完整的排查路径见[元数据变更后的缓存失效](../reference/maintainer/cache-invalidation.md)。 + +## 这个机制不是什么 + +- **不是公开集成通道。** 外部集成方不应向这些 broker 发布消息,也不应订阅它们。它们是集群内部的扇出机制。 +- **不是失效缓存的唯一方式。** `xlyEntry` 的 HTTP 写路径已经会在需要时发布 JMS 事件;手动触发只用于边界情况。 diff --git a/zh/docs/api-reference/notifications.md b/zh/docs/api-reference/notifications.md new file mode 100644 index 0000000..a0a34af --- /dev/null +++ b/zh/docs/api-reference/notifications.md @@ -0,0 +1,38 @@ +# 通知(xlyMsg) + +出站通知(钉钉和微信)通过 `xlyMsg` 模块完成。它**不是**调用方直接访问的 HTTP 接口面,而是范围内服务在业务事件需要向聊天平台推送消息时调用的内部 SDK。 + +## 模块内容 + +`xlyMsg/src/main/java/com/xly/`: + +| Package | 角色 | +|---|---| +| `dingtalk/service/DingTalkService` + `dingtalk/util/SendDingTalkUtil`、`DingTalkMsgContentUtil`、`LocalCacheClient` | 钉钉企业消息分发。封装 `com.aliyun:dingtalk:2.1.14` 和 `com.aliyun:alibaba-dingtalk-service-sdk:2.0.0`。 | +| `wechat/service/WechatService` + `wechat/util/SendWxUtil`、`Wx_SignatureUtil`、`JedisMsgUtil`、`MsgContentUtil`、`Xml2JsonUtil` | 微信工作平台分发,包含签名和发送,以及 Redis 支撑的 access-token 缓存。 | +| `notice/service/NoticeService` | 与供应商无关的通知抽象;把“通知用户 X 某事件 Y”的逻辑路由到正确后端。 | + +`xlyMsg/build.gradle` 中唯一的框架依赖是 `xlyPersist`。该模块作为库被消费,不会作为自己的服务部署。 + +## 谁在调用它(范围内调用方) + +这些调用让 `xlyMsg` 成为框架相关内容,而不是 plat 层内容: + +| Caller | 作用 | +|---|---| +| `xlyEntry/.../web/businessweb/TestController.java` | 诊断端点,用于发送测试钉钉消息。 | +| `xlyBusinessService/.../thread/UpdateDingTalkThread.java` | 单据变更后推送钉钉更新的异步 worker。 | +| `xlyBusinessService/.../service/impl/CheckExamineFlowServiceImpl.java` | 工作流审批通知;Activiti 任务被重新分配或完成时,待办人会收到聊天消息。 | +| `xlyBusinessService/.../service/impl/GenericProcedureCallServiceImpl.java` | 通用存储过程 hook:任何选择接入的 `gdsmodule` 存储过程都可以通过这条路径发布通知。 | +| `xlyInterface/.../util/DingTalkUtil.java` + `scheduler/ScheduledTasks.java`、`ErpJobRunStatus.java` | 集成侧的定时任务心跳 / 失败告警。 | + +## 配置 + +凭据(钉钉企业应用 key/secret、微信 AppID、agent ID)从各环境 yaml profile 读取。这里没有全局默认值,每个客户部署会填自己的值,所以框架 Wiki 无法给出规范示例。查看 `xlyEntry/src/main/resources/application-.yml` 下的 active profile yaml,确认实际消费的 key。 + +微信路径使用 Redis 缓存 access token(`wechat/util/JedisMsgUtil.java`),和框架其他部分使用同一个 Redis。哪些服务共享哪些 Redis,见[多服务部署](../reference/maintainer/deployment.md)。 + +## 这个机制不是什么 + +- **不是**入站 webhook 接收器。钉钉回调(例如审批按钮点击)和其他 [webhook](webhooks.md) 一样走 `xlyInterface`。 +- **不是** `xlyPlat*` 的推送通知路径。plat 层有自己的通道(`xlyPlatSmsService`、JPush、App 侧推送),本 Wiki 将其视为范围外。 diff --git a/zh/docs/api-reference/webhooks.md b/zh/docs/api-reference/webhooks.md new file mode 100644 index 0000000..8b8df72 --- /dev/null +++ b/zh/docs/api-reference/webhooks.md @@ -0,0 +1,51 @@ +# 入站 Webhook(`xlyInterface`) + +`xlyInterface` 是三个 Spring Boot WAR 中最小的一个。它的职责是**接收第三方系统推送进来的 HTTP 请求**,例如微信、伙伴工厂、供应商门户,并把它们路由到 xly 处理器。入口类为 `xlyInterface/src/main/java/com/xly/InterfaceApplicationBoot.java`,context-path 为 `/xlyInterface`。 + +这是三个服务里唯一内置 **Swagger UI** 的服务,因为面向伙伴的受众最适合使用可交互的试调文档。构建依赖 `io.springfox:springfox-swagger-ui:2.9.2` 和 `io.springfox:springfox-swagger2:2.9.2`。服务运行后,UI 使用 SpringFox 默认地址: + +```text +http:///xlyInterface/swagger-ui.html +``` + +等价的 JSON 描述位于 `http:///xlyInterface/v2/api-docs`。 + +## 数据驱动接收器:`/interfaceDefine/*` + +它和[外部 API 的 `/api/invoke`](external.md) 模式对应,但用于入站调用: + +| Endpoint | Method | 用途 | +|---|---|---| +| `/interfaceDefine/invoke/{interfaceInvoke}` | POST | 将入站载荷分发给 `{interfaceInvoke}` 命名的处理器。 | +| `/interfaceDefine/callthirdparty/{interfaceInvoke}` | POST | 转发到已配置的出站端点。(对应 `xlyApi` 上同样存在的 `/interfaceDefine/callthirdparty/...` 端点;这里的入站侧和数据驱动入站分发器配套。) | + +处理器:`xlyInterface/src/main/java/com/xly/web/InterfaceController.java`。 + +`{interfaceInvoke}` path variable 会查找一条元数据行,该行声明要运行的 SQL 或存储过程、参数映射和响应形状。它和 xly 其他部分一样遵循数据驱动思路:新增入站端点靠插入数据行,而不是写 Java。 + +## 硬编码的供应商接收器 + +少量接收器不会经过 `/interfaceDefine`,因为它们必须匹配合作方固定的 URL 规格。 + +| Endpoint | Method | 用途 | +|---|---|---| +| `/Push` | POST | 供应商(微信 / 类似系统)推送接收器。 | +| `/Pull` | POST | 供应商拉取模式接收器。 | +| `/getKey/{key}` | GET | 按伙伴命名的 `key` 获取公钥。 | +| `/getKeyTest` | GET | `/getKey` 的测试模式变体。 | +| `/send/sendQw` | POST | 企业微信出站消息。 | + +处理器:`xlyInterface/src/main/java/com/xly/web/WX_VendorWeb.java` 和 `xlyInterface/src/main/java/com/xly/wechat/test/SendQwController.java`。 + +## 认证 + +Webhook 认证方式按伙伴而异。这里没有统一的 token 签发端点(不同于 `xlyApi`)。每个处理器选择自己的方案:签名 header、查询参数 secret,或基于 `sysapibrand` 的预共享 key 校验。接入前需要阅读对应控制器。 + +## 元数据放在哪里 + +`/interfaceDefine/*` 分发器会查询命名入站处理器的元数据行。可检查 `sysapi` 表族(`sysapi`、`sysapibrand`、`sysapithirdparty`)以及当前 schema 自动目录中的任何 `interface*` 表。账本信息和[外部 API](external.md) 有重叠:两个服务共享 API 定义数据,只是暴露在不同 URL 上。 + +## 这个服务不是什么 + +- **不是用 xly 的 session cookie 认证。** 第三方调用方没有用户 session。认证是每个处理器自己的事情,不是框架级统一机制。 +- **不是出站调用通道。** 出站分发(xly 调用伙伴)走 `xlyApi` 的 `/interfaceDefine/callthirdparty/*` 或 `/thirdparty/*` 控制器。这里有重复是有意为之:入站和出站有完全不同的安全姿态。 diff --git a/zh/docs/concepts/api-surface.md b/zh/docs/concepts/api-surface.md new file mode 100644 index 0000000..a56ac98 --- /dev/null +++ b/zh/docs/concepts/api-surface.md @@ -0,0 +1,32 @@ +# 三层 API + +xly 不是只有一个 API,而是有**三层**,分别由三个独立的 Spring Boot 服务承载,面向不同受众,也有不同的运行契约。任何集成讨论的第一步,都是先确认你在和哪一层对话。 + +| 层级 | 服务 | Context path | 受众 | +|---|---|---|---| +| **内部** | `xlyEntry` | `/xlyEntry` | xly SPA 自身(BACK + FROUNT)。对外部调用方不是稳定契约。 | +| **外部** | `xlyApi` | `/xlyApi` | 从外部调用 xly 的租户和集成方。数据驱动的公开 API。 | +| **入站 webhook** | `xlyInterface` | `/xlyInterface` | 将事件推送进 xly 的第三方系统。带 Swagger。 | + +每个服务都会构建成自己的 WAR,并在自己的 JVM 中运行。它们不共享进程内状态;它们共享的是**数据库**。正是这个共享数据库让服务拆分成立:内部 API 写入的数据会自动被外部 API 读到,因为两者都连接同一个 schema。 + +## 为什么是三层,而不是一层 + +每一层回答的问题不同,合在一起会牺牲清晰度: + +- **内部层**很大(对所有元数据驱动模块做通用 CRUD)、易变(随框架变化)、且有意保持弱类型(SPA 决定要什么,服务端照元数据执行)。 +- **外部层**是收敛后的接口(只暴露允许集成方使用的端点),按 `sApiCode` 做版本化,并用 bearer token 认证。它能跨框架变化保持稳定,正是因为它小而明确。 +- **入站 webhook 层**接收来自第三方系统的不可信 body,并路由到 xly 处理器。Swagger UI 放在这里,因为这个受众最需要交互式文档。 + +## 每一层在运行时长什么样 + +- **内部层** — 见[四表读取](request-lifecycle.md)。一个端点(`/business/getModelBysId`)返回完整表单布局;另一个端点(`/business/addUpdateDelBusinessData`)写入元数据命名的任意表中的任意行。端点少,形状通用。 +- **外部层** — 大多数调用走 `/api/invoke/{sApiCode}`。`sApiCode` 是 `sysapi` 元数据表中的一行,定义 SQL 模板、参数、认证要求和目标。新的外部 API 是**注册成数据**,不是写成代码;这和框架对自身表单采用的数据驱动基本论点一致。 +- **入站 webhook 层** — `/interfaceDefine/invoke/{interfaceInvoke}` 接收载荷,查找元数据中的匹配处理器,运行配置好的 SQL 或存储过程。此外还有少量为特定伙伴准备的硬编码接收器(`/Push`、`/Pull`、`/send/sendQw`)。 + +## 下一步看哪里 + +- [API 参考(内部)](../api-reference/internal.md) — SPA 使用的 `xlyEntry` 端点;不面向外部调用方。 +- [API 参考(外部)](../api-reference/external.md) — 集成方使用的 `xlyApi` 接口面,以及 `sysapi` 驱动的 `/api/invoke` 模式。 +- [API 参考(Webhook)](../api-reference/webhooks.md) — `xlyInterface` 入站接口面,以及 Swagger UI 的服务地址。 +- [API 参考(消息)](../api-reference/messaging.md) — 补充 HTTP API 的 ActiveMQ / RocketMQ 事件通道。 diff --git a/zh/docs/concepts/index.md b/zh/docs/concepts/index.md index 6830962..2d96189 100644 --- a/zh/docs/concepts/index.md +++ b/zh/docs/concepts/index.md @@ -4,6 +4,59 @@ 框架是模块驱动的:PM 创建的每个页面、客户看到的每个页面,都是 `gdsmodule` 中的一行,并与 `gdsconfigformmaster/slave` 中的布局行、`gdsjurisdiction` 中的权限行,以及运行时通过 `BusinessBaseController` 调用的存储过程组合在一起。 +## 总览 + +```mermaid +flowchart TB + classDef oos stroke-dasharray:5 5,color:#999 + + BACK["BACK
配置 UI"] + FROUNT["FROUNT
客户 UI"] + EXT([外部集成方]) + HOOKS([第三方 webhook]) + + subgraph framework [框架运行时 - 本 Wiki 覆盖] + XENTRY[xlyEntry] + XAPI[xlyApi] + XIF[xlyInterface] + XFLOW[xlyFlow] + XPLC[xlyPlc] + XMSG[/"xlyMsg
库"/] + end + + DB[("MySQL
xlyweberp")] + REDIS[(Redis)] + AMQ([ActiveMQ]) + XEJMSC[xlyErpJmsConsumer] + PLAT[("xlyPlat* 模块
+ MongoDB")]:::oos + + BACK --> XENTRY + FROUNT --> XENTRY + FROUNT --> XAPI + EXT --> XAPI + HOOKS --> XIF + + XENTRY --> DB + XAPI --> DB + XIF --> DB + XFLOW --> DB + XPLC --> DB + + XENTRY <--> REDIS + XENTRY -- "元数据变更" --> AMQ + AMQ --> XEJMSC + XEJMSC --> REDIS + + XENTRY -. 使用 .-> XMSG + XIF -. 使用 .-> XMSG + + PLAT -.-> DB +``` + +虚线簇(`xlyPlat*` + MongoDB)是 B2B 印刷平台层。它存在于构建中,但在本 Wiki 中[不属于覆盖范围](../index.md)。 + +## 概念页 + 这些页面刻意保持简短:每页解释一个概念,并链接到实际使用该概念的[垂直切片](../slices/index.md)。概念页应该短;如果一页长到超过一屏,通常说明它应该变成一个切片。 - [数据驱动的基本论点](thesis.md) — 框架为什么长成这样。 @@ -14,3 +67,4 @@ - [定制层级](customization-layers.md) — 通道 1 内,基础 / 租户 / 用户覆盖如何合并。 - [多租户与产品版本](multi-tenancy.md) — 三条作用域轴(`sBrandsId`、`sSubsidiaryId`、`sVersionFlowId`)。 - [元数据驱动的请求生命周期](request-lifecycle.md) — 后续会反复回到这张图。 +- [三层 API](api-surface.md) — 内部(`xlyEntry`)、外部(`xlyApi`)、入站 webhook(`xlyInterface`)。 diff --git a/zh/docs/index.md b/zh/docs/index.md index eeabc6d..76068c9 100644 --- a/zh/docs/index.md +++ b/zh/docs/index.md @@ -26,6 +26,7 @@ - 人脸识别(`xlyFace`),范围较窄。 - `xlyweberp_*` 数据库之间的租户级 schema 漂移;本 Wiki 针对一个 schema。 - 备份表(`*_bak`、`*0302` 等)。 +- MongoDB 文档存储(yaml profile 中的 `spring.data.mongodb.uri`,以及 `xlyEntity/.../mongo/` 下的文档类)。每个 `@Document` 类都是 `PLAT_*` 命名,每个 `MongoTemplate` 调用方都位于 `xlyPlat*` 模块中,因此 MongoDB 属于上面的 plat 层。本 Wiki 覆盖的框架层只讨论 MySQL。 ## 如何修正这个 Wiki diff --git a/zh/docs/reference/maintainer/index.md b/zh/docs/reference/maintainer/index.md index d9fd969..e178b23 100644 --- a/zh/docs/reference/maintainer/index.md +++ b/zh/docs/reference/maintainer/index.md @@ -4,6 +4,7 @@ 本章服务于会修改 `BusinessBaseController`、向 `gds*` 表族添加元数据表,或接入新第三方集成的人。 +- [本地运行 xlyEntry](running-locally.md) — 开发者首次启动配置(前置条件、profile、DB 覆盖、冒烟检查)。 - [运行时:BusinessBaseController 及相关组件](runtime.md) — 元数据驱动分发循环。 - [通用存储过程分发](proc-dispatch.md) — `GenericProcedureCallController` 深入说明。 - [元数据变更后的缓存失效](cache-invalidation.md) — `ConsumerChangeGdsModuleThread` 及相关组件。 diff --git a/zh/docs/reference/maintainer/running-locally.md b/zh/docs/reference/maintainer/running-locally.md new file mode 100644 index 0000000..e78cb18 --- /dev/null +++ b/zh/docs/reference/maintainer/running-locally.md @@ -0,0 +1,63 @@ +# 本地运行 xlyEntry + +本页是开发者上手路径:克隆仓库,在本机启动 `xlyEntry`,并访问一个框架端点。配套的[多服务部署](deployment.md)页面覆盖运维视角(多服务、profile 组合、nginx)。 + +## 前置条件 + +| 工具 | 版本说明 | +|---|---| +| JDK 8 | `xly-src/build.gradle` 固定 `sourceCompatibility = 1.8`。JDK 11+ 尚未验证。 | +| Gradle wrapper | 仓库已包含(`./gradlew`)。不要单独安装 Gradle。 | +| MySQL 8 | 驱动为 `com.mysql.cj.jdbc.Driver`。你的 DB schema 必须和 active profile yaml 指向的 schema 匹配;见下面“DB 覆盖”。 | +| Redis | `local` profile 期望 `127.0.0.1:16379`,密码为 `xlyXLY2015`(见 `xlyEntry/src/main/resources/application-local.yml`)。 | +| ActiveMQ + MongoDB | `local` profile 中两者都指向 `t0.xlyprint.com`。如果你的网络能访问该主机,就不需要本地运行它们。 | + +自动目录生成器(`scripts/gen_catalog.py`)和运行 xly 无关;它只通过 `~/.my.cnf` 读取 schema 元数据。该路径见[参与维护](../../contributing/index.md)。 + +## DB 覆盖 + +仓库中的 `xlyEntry/src/main/resources/application-local.yml` 的 `spring.datasource.url` 指向远程开发库。大多数开发者会把它覆盖到本地或个人目标。有两个选择: + +1. **直接编辑 yaml。** 不要提交这个改动。 +2. **通过 JVM 参数覆盖。** 调用 `bootRun` 时传 `-Dspring.datasource.url=...`。 + +Wiki 的侦察脚本通过 `~/.my.cnf` 使用 `xlyweberp_saas_ai`,这和 `application-local.yml` 默认的 `xlyweberp_saas` **不是同一个** schema。选一个 schema,并保持一致。 + +## 运行 + +从 `xly-src/` 目录执行: + +```bash +./gradlew :xlyEntry:bootRun -Dspring.profiles.active=local +``` + +`xlyEntry` 会打成 WAR(见它自己的 `build.gradle`),但开发时 `bootRun` 使用的是 Spring Boot 内嵌 Tomcat,因此不需要外部 Tomcat 部署。 + +## 冒烟检查 + +启动完成后(日志中出现 `Tomcat started on port(s): 8080`): + +| URL | 预期结果 | +|---|---| +| `http://localhost:8080/xlyEntry/` | 框架落地页。未认证页面可以渲染;需要认证的页面会跳到 `/login`。 | +| `http://localhost:8080/xlyEntry/druid/` | Druid 数据源监控。默认凭据 `xly` / `666666` 在 yaml 中;对外暴露端口前必须修改。 | + +如果冒烟检查失败,查看 active profile yaml 中 `logging.dirpath` 配置的日志目录。 + +## `local` 包含什么,不包含什么 + +`local` profile **只**启动框架运行时。它不会启动: + +- `xlyApi` — 如果需要外部 API 接口面,请作为第二个 JVM 启动。 +- `xlyInterface` — 同上。 +- `xlyPlc`、`xlyFlow`、`xlyFace` — 同上;每个服务都有自己的 application class 和 profile。 +- `xlyErpJms*` consumer — 它们是独立的 Spring Boot app。没有它们,JMS 驱动的缓存失效不会跨节点发生(单节点开发机通常可以接受)。 + +多服务本地开发见[多服务部署](deployment.md)。 + +## 启动失败时 + +最常见的两类失败: + +1. **`Communications link failure`** — `spring.datasource.url` 目标在你的机器上不可达。按“DB 覆盖”中的方法覆盖它。 +2. **`Connection refused: t0.xlyprint.com:61616` 或 `:27017`** — 你的网络无法访问共享开发基础设施。你可以接入正确网络,把 yaml 指向本地实例,或在调试期间注释掉相关 bean。 diff --git a/zh/mkdocs.yml b/zh/mkdocs.yml index b31c471..c1fe98d 100644 --- a/zh/mkdocs.yml +++ b/zh/mkdocs.yml @@ -81,6 +81,7 @@ nav: - 定制层级: concepts/customization-layers.md - 多租户与产品版本: concepts/multi-tenancy.md - 元数据驱动的请求生命周期: concepts/request-lifecycle.md + - 三层 API: concepts/api-surface.md - 2. 垂直切片: - slices/index.md - "切片 1:CRUD 模块(Hello World)": slices/01-hello-world.md @@ -98,21 +99,29 @@ nav: - "如何设置权限": reference/builder/permissions.md - 4. 参考(维护人员): - reference/maintainer/index.md + - "本地运行 xlyEntry": reference/maintainer/running-locally.md - "运行时:BusinessBaseController 及相关组件": reference/maintainer/runtime.md - "通用存储过程分发": reference/maintainer/proc-dispatch.md - "元数据变更后的缓存失效": reference/maintainer/cache-invalidation.md - "SQL 模板(xlyEntry/templesql/)": reference/maintainer/sql-templates.md - "多服务部署": reference/maintainer/deployment.md - "Activiti 集成": reference/maintainer/activiti.md - - 5. 自动目录: + - 5. API 参考: + - api-reference/index.md + - "内部 API(xlyEntry)": api-reference/internal.md + - "外部 API(xlyApi)": api-reference/external.md + - "入站 Webhook(xlyInterface)": api-reference/webhooks.md + - "消息(ActiveMQ / RocketMQ)": api-reference/messaging.md + - "通知(xlyMsg)": api-reference/notifications.md + - 6. 自动目录: - auto-catalog/index.md - 数据表: auto-catalog/tables/index.md - 视图: auto-catalog/views/index.md - 存储过程: auto-catalog/procedures/index.md - 函数: auto-catalog/functions/index.md - - 6. 术语表: + - 7. 术语表: - glossary/index.md - - 7. 参与维护: + - 8. 参与维护: - contributing/index.md extra: -- libgit2 0.22.2