Commit 24cae0bdc427a0df087a4c7249d0b84e2e1331f6
1 parent
40f01805
feat(reference): hello-world printing-shop plug-in (PF4J, depends only on api.v1)
Showing
5 changed files
with
164 additions
and
0 deletions
reference-customer/plugin-printing-shop/build.gradle.kts
0 → 100644
| 1 | +plugins { | ||
| 2 | + alias(libs.plugins.kotlin.jvm) | ||
| 3 | +} | ||
| 4 | + | ||
| 5 | +description = "vibe_erp reference plug-in: a hello-world printing-shop customer expressed entirely through api.v1." | ||
| 6 | + | ||
| 7 | +java { | ||
| 8 | + toolchain { | ||
| 9 | + languageVersion.set(JavaLanguageVersion.of(21)) | ||
| 10 | + } | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +kotlin { | ||
| 14 | + jvmToolchain(21) | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +// CRITICAL: this plug-in may depend on api/api-v1 ONLY. The root | ||
| 18 | +// build.gradle.kts enforces this; adding `:platform:*` or `:pbc:*` here | ||
| 19 | +// will fail the build with an architectural-violation error. | ||
| 20 | +// | ||
| 21 | +// This restriction is the entire point of having a "reference plug-in" | ||
| 22 | +// in the repo: it is the executable proof that the framework can express | ||
| 23 | +// a real customer's workflow using only the public plug-in API. If this | ||
| 24 | +// plug-in needs more than api.v1 offers, the right answer is to grow | ||
| 25 | +// api.v1 deliberately, NOT to reach into platform internals. | ||
| 26 | +dependencies { | ||
| 27 | + implementation(project(":api:api-v1")) | ||
| 28 | + implementation(libs.kotlin.stdlib) | ||
| 29 | + compileOnly(libs.pf4j) // for @Extension annotations; provided by host at runtime | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +// Package this as a "fat plug-in JAR" — PF4J expects a single JAR with the | ||
| 33 | +// plug-in's manifest in META-INF and all of its (non-host) dependencies | ||
| 34 | +// inside it. Since this plug-in only depends on api.v1 (which is provided by | ||
| 35 | +// the host) and the Kotlin stdlib (also provided), the resulting JAR is tiny. | ||
| 36 | +tasks.jar { | ||
| 37 | + duplicatesStrategy = DuplicatesStrategy.EXCLUDE | ||
| 38 | + manifest { | ||
| 39 | + attributes( | ||
| 40 | + "Plugin-Id" to "printing-shop", | ||
| 41 | + "Plugin-Version" to project.version, | ||
| 42 | + "Plugin-Provider" to "vibe_erp reference", | ||
| 43 | + "Plugin-Class" to "org.vibeerp.reference.printingshop.PrintingShopPlugin", | ||
| 44 | + "Plugin-Description" to "Reference printing-shop customer plug-in", | ||
| 45 | + "Plugin-Requires" to "1.x", | ||
| 46 | + ) | ||
| 47 | + } | ||
| 48 | + // src/main/resources is already on the resource path automatically; no | ||
| 49 | + // need for an explicit `from(...)` block — that was causing the | ||
| 50 | + // "duplicate plugin.yml" error because Gradle would try to copy it twice. | ||
| 51 | +} |
reference-customer/plugin-printing-shop/src/main/kotlin/org/vibeerp/reference/printingshop/PrintingShopPlugin.kt
0 → 100644
| 1 | +package org.vibeerp.reference.printingshop | ||
| 2 | + | ||
| 3 | +import org.vibeerp.api.v1.plugin.Plugin | ||
| 4 | +import org.vibeerp.api.v1.plugin.PluginContext | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * The reference printing-shop plug-in. | ||
| 8 | + * | ||
| 9 | + * This class is the **executable acceptance test** for the entire vibe_erp | ||
| 10 | + * framework. If this plug-in cannot express the workflows in | ||
| 11 | + * `raw/业务流程设计文档/` using ONLY `org.vibeerp.api.v1.*`, the framework's | ||
| 12 | + * extension surface is wrong and must be reshaped — see CLAUDE.md guardrail #1 | ||
| 13 | + * and the architecture spec section 13 ("Verification"). | ||
| 14 | + * | ||
| 15 | + * What this v0.1 incarnation actually does: | ||
| 16 | + * 1. Logs `started` / `stopped` so the operator can confirm the loader | ||
| 17 | + * picked it up. | ||
| 18 | + * 2. Holds onto the [PluginContext] so future versions can publish events, | ||
| 19 | + * register `TaskHandler`s, and register HTTP endpoints. | ||
| 20 | + * | ||
| 21 | + * What it does NOT yet do (deferred to v0.2 of the reference plug-in): | ||
| 22 | + * • register a workflow task handler for "compute job card from quote" | ||
| 23 | + * • register HTTP endpoints under /api/v1/plugins/printing-shop/... | ||
| 24 | + * • subscribe to OrderCreated events from pbc-orders-sales | ||
| 25 | + * • register custom permission checks | ||
| 26 | + * | ||
| 27 | + * Each of those deferred items maps to growing `api.v1` precisely once for a | ||
| 28 | + * real reason — exactly the discipline the architecture demands. | ||
| 29 | + */ | ||
| 30 | +class PrintingShopPlugin : Plugin { | ||
| 31 | + | ||
| 32 | + private var context: PluginContext? = null | ||
| 33 | + | ||
| 34 | + override fun id(): String = "printing-shop" | ||
| 35 | + | ||
| 36 | + override fun version(): String = "0.1.0-SNAPSHOT" | ||
| 37 | + | ||
| 38 | + override fun start(context: PluginContext) { | ||
| 39 | + this.context = context | ||
| 40 | + context.logger.info("printing-shop plug-in started — reference acceptance test active") | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + override fun stop() { | ||
| 44 | + context?.logger?.info("printing-shop plug-in stopped") | ||
| 45 | + context = null | ||
| 46 | + } | ||
| 47 | +} |
reference-customer/plugin-printing-shop/src/main/resources/i18n/messages_en-US.properties
0 → 100644
| 1 | +# Reference printing-shop plug-in — English (US) message bundle. | ||
| 2 | +# | ||
| 3 | +# Keys follow the convention <plugin-id>.<area>.<message> declared in | ||
| 4 | +# CLAUDE.md guardrail #6 / architecture spec section 7. The plug-in id | ||
| 5 | +# prefix is critical: it stops two plug-ins from accidentally colliding | ||
| 6 | +# on a generic key like "form.title". | ||
| 7 | + | ||
| 8 | +printingshop.plate.title=Plate | ||
| 9 | +printingshop.plate.create=Create plate | ||
| 10 | +printingshop.plate.approve=Approve plate | ||
| 11 | +printingshop.job.title=Job card | ||
| 12 | +printingshop.job.dispatch=Dispatch to press | ||
| 13 | +printingshop.workflow.quoteToJobCard.name=Quote to job card |
reference-customer/plugin-printing-shop/src/main/resources/i18n/messages_zh-CN.properties
0 → 100644
| 1 | +# Reference printing-shop plug-in — Simplified Chinese message bundle. | ||
| 2 | +# UTF-8 encoded; Spring's MessageSource is configured to read .properties | ||
| 3 | +# files as UTF-8 in api.v1 / platform-i18n. | ||
| 4 | + | ||
| 5 | +printingshop.plate.title=印版 | ||
| 6 | +printingshop.plate.create=新建印版 | ||
| 7 | +printingshop.plate.approve=审批印版 | ||
| 8 | +printingshop.job.title=工单 | ||
| 9 | +printingshop.job.dispatch=派工至印刷机 | ||
| 10 | +printingshop.workflow.quoteToJobCard.name=报价转工单 |
reference-customer/plugin-printing-shop/src/main/resources/plugin.yml
0 → 100644
| 1 | +# vibe_erp plug-in manifest for the reference printing-shop customer. | ||
| 2 | +# | ||
| 3 | +# This file is what a real customer's plug-in would look like. It is loaded | ||
| 4 | +# by the platform-plugins host, validated, and used to wire the plug-in into | ||
| 5 | +# its own classloader and Spring child context. | ||
| 6 | +# | ||
| 7 | +# The id is dotless and url-safe; it becomes the schema-namespace prefix | ||
| 8 | +# (`plugin_printingshop__*`) and the URL prefix for any plug-in endpoints | ||
| 9 | +# (`/api/v1/plugins/printing-shop/...`). | ||
| 10 | +id: printing-shop | ||
| 11 | +version: 0.1.0-SNAPSHOT | ||
| 12 | + | ||
| 13 | +# Semver range against api.v1 — the host loader rejects this plug-in at | ||
| 14 | +# install time if its api.v1 major doesn't match. "1.x" means "any 1.y.z". | ||
| 15 | +requiresApi: "1.x" | ||
| 16 | + | ||
| 17 | +# Translated names. The host displays the entry matching the operator's | ||
| 18 | +# locale; falls back to en-US. | ||
| 19 | +name: | ||
| 20 | + en-US: Printing Shop (reference customer) | ||
| 21 | + zh-CN: 印刷厂(参考客户) | ||
| 22 | + de-DE: Druckerei (Referenzkunde) | ||
| 23 | + | ||
| 24 | +# Permissions this plug-in declares. They appear in the role editor for | ||
| 25 | +# every tenant that has this plug-in enabled. | ||
| 26 | +permissions: | ||
| 27 | + - printingshop.plate.view | ||
| 28 | + - printingshop.plate.create | ||
| 29 | + - printingshop.plate.approve | ||
| 30 | + - printingshop.job.dispatch | ||
| 31 | + | ||
| 32 | +# Other plug-ins that must be present and started before this one. | ||
| 33 | +dependencies: [] | ||
| 34 | + | ||
| 35 | +# Resources to load from inside the JAR after the plug-in starts. Paths are | ||
| 36 | +# relative to the JAR root and the file extensions tell the host how to | ||
| 37 | +# treat them. | ||
| 38 | +metadata: | ||
| 39 | + - workflows/quote-to-job-card.bpmn | ||
| 40 | + - forms/job-card.form.json | ||
| 41 | + - metadata/seed.yml | ||
| 42 | + - i18n/messages_en-US.properties | ||
| 43 | + - i18n/messages_zh-CN.properties |