Commit 24cae0bdc427a0df087a4c7249d0b84e2e1331f6

Authored by vibe_erp
1 parent 40f01805

feat(reference): hello-world printing-shop plug-in (PF4J, depends only on api.v1)

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