Ako Deedspan funguje
Deedspan prenáša dáta medzi e-shopom a jeho ERP, skladom, účtovníctvom, PIM alebo CRM. Každý systém sa napája na spoločný dátový model nezávislý od e-shopu. Každé prostredie obchodníka má vlastnú inštaláciu, staging a produkcia sú dve inštalácie toho istého projektu.
Čo sa stane so záznamom
- Spúšťač začne beh flow.
- Načíta sa zdroj: súbory, výsledok dopytu, odpoveď API alebo dáta z webhooku.
- Každý riadok sa namapuje na záznam spoločného modelu, napríklad
stock_level. - Voliteľné PHP hooky záznamy upravia, rozdelia alebo preskočia.
- Nezmenené záznamy sa označia a neposielajú sa.
- Cieľ overí, že odkazované SKU existujú.
- Konektor zapisuje hromadne, najviac 1 000 záznamov v dávke.
- Pri každom zázname sa uloží výsledok.
Z čoho sa inštalácia skladá
| Časť | Na čo slúži |
|---|---|
| Aplikácia | Jadro (deedhold/deedspan, Symfony bundle) a váš projekt |
| Workery | Spúšťajú behy, spracúvajú dávky a behy dokončujú; môže ich bežať viac |
| Plánovač | Spúšťa flow podľa cronu; beží práve jeden |
| MariaDB | Behy, výsledky záznamov, čo sa naposledy odoslalo, dáta správ, šifrované pripojenia |
| RabbitMQ | Povinný; nesie všetky fázy behu |
| Archív súborov | Šifrované súbory na disku alebo v úložisku kompatibilnom s S3 |
| Modul pre Magento | Zachytávanie zmien, podpísané upozornenia, hromadný zápis skladu, stĺpec v prehľade objednávok |
Čo obsahuje projekt
Malý projekt pre Composer založený z kostry. Je v git repozitári obchodníka a nasadzuje ho CI.
acme-integrations/
deedspan.yaml pripojenia a balíky typov záznamov
flows/ jeden YAML súbor na každý flow
records/ vlastné typy záznamov a polia navyše
connectors/ konektory len pre tento projekt
lookups/ prevodové tabuľky v CSV
src/ PHP hooky, operácie, prenosy
samples/ vzorové súbory pre skúšky naprázdno
composer.json jadro a doplnky; composer.lock je v gite
Dockerfile runtime image a vaše balíky
compose.yaml app, worker, scheduler, MariaDB, RabbitMQ
deedspan.yaml
Pripojenia k ostatným systémom a vypnuté balíky typov záznamov. Pri prístupových údajoch je len názov premennej prostredia, takže sa heslá nedostanú do gitu, do flow ani k hookom.
deedspan.yaml
connections:
erp-ftp:
settings: { host: sftp.erp.example, user: deedspan, root: /upload }
secrets: { password: ERP_FTP_PASSWORD }
shop:
settings: { url: "https://shop.example.com" }
secrets:
consumer_key: MAGENTO_CONSUMER_KEY
consumer_secret: MAGENTO_CONSUMER_SECRET
token: MAGENTO_TOKEN
token_secret: MAGENTO_TOKEN_SECRET
records:
disabled: [pricing]
Prevodové tabuľky
CSV s hlavičkou key,value, z ktorého číta lookup().
lookups/warehouses.csv
key,value
01,main
02,east
Základné pojmy
Flow a spúšťanie
Každá integrácia sa skladá z jedného alebo viacerých flow. Flow prenáša jeden typ záznamov jedným smerom: zdroj, mapovanie, voliteľné hooky a cieľ. Uvádza, ktorému systému dáta patria (owner:), a dva flow nesmú zapisovať rovnaké pole do rovnakého cieľa.
flows/prices-from-erp.yaml
flow: prices-from-erp
owner: erp
record: price
triggers:
schedule: "0 * * * *"
source:
transport: sftp
connection: erp-ftp
path: /export/prices_*.csv
format: { type: csv, delimiter: ";" }
map:
sku: "trim(row['sku'])"
channel: "lower(trim(row['channel']))"
base: "decimal(row['price'], ',')"
currency: "upper(trim(row['currency']))"
target:
connector: magento
operation: price.update
connection: shop
channels: { eu: eu_store, uk: uk_store }
rules: { unknown_references: fail, compare_with: target }
Flow sa spúšťa jedným zo štyroch spôsobov, ktoré sa zapisujú pod triggers:.
| Spúšťač | Flow sa spustí, keď |
|---|---|
schedule | nastane čas podľa cronu, napríklad "*/15 * * * *" |
shop_signal | modul v Magente ohlási zmenené objednávky alebo produkty |
webhook | iný systém zavolá podpísaný POST /webhooks/<name> |
| ručne | vždy: z príkazového riadka, cez API, naprázdno alebo ako opakovanie |
Webhook má dva režimy. V režime signal len oznámi, čo sa zmenilo, a flow si aktuálne dáta načíta sám, takže duplicitné alebo oneskorené volania nevadia. V režime payload nesie dáta priamo volanie; uložia sa a flow ich číta cez source: { transport: webhook }. Webhooky sa občas stratia, preto k nim nechajte bežať aj pomalý plán:
triggers
triggers:
webhook: { name: payments, mode: payload }
schedule: "0 3 * * *" # nočná kontrola
| Kľúč | Obsahuje |
|---|---|
source | Prenos a formát, alebo stream konektora. group_by spojí susedné riadky do jedného záznamu. |
map | Jeden výraz na každé pole. |
hooks | PHP triedy v bodoch after_read, after_map, before_send, on_error. |
target | Operácia konektora, alebo prenos, ktorý zapíše súbory, zavolá webhook alebo naplní medzitabuľku. |
rules | unknown_references, compare_with a bezpečnostné limity ako max_change_share. |
Každý flow sa kontroluje pri načítaní projektu, takže chyby zachytí už CI.
Záznamy
Záznam je jedna prenášaná položka, napríklad stav TENT-200 na skladovom mieste main. Typy záznamov sú definície v YAML s kľúčom a typovanými poľami.
| Typ | Kľúč | Hlavné polia |
|---|---|---|
stock_level | sku + location | množstvo, na sklade, dátum naskladnenia |
price | sku + channel | základná cena, mena, množstevné a akciové ceny |
product | sku | typ, sada atribútov, kanály, kategórie, viditeľnosť, varianty, súvisiace |
category | názov | preložené názvy |
order | číslo objednávky | zákazník, adresy, položky, súčty, platba, doprava, stav |
order_update | číslo objednávky | stav, zásielka so sledovacím číslom, faktúra |
- Záznam nesie len polia, ktoré flow mapuje. Chýbajúce pole zostane bez zmeny,
nullho vymaže a prázdna bunka v CSV dáta nikdy nezmaže. - Pole môže mať hodnotu pre každý predajný kanál alebo jazyk.
extends:pridá polia k typu z jadra.references: productzabezpečí kontrolu, že SKU v cieli existuje.
Konektory
| Časť | Možnosti |
|---|---|
| Prenos | sftp, ftp, local, http, soap, mssql a ďalšie databázy, email, s3, webhook |
| Formát | csv (akékoľvek kódovanie), xml, json, jsonl, pevná šírka |
| Konektor systému | magento (jadro); Pohoda, Helios, Fakturoid (doplnky); vlastné |
Operácie pre Magento: stock.update, price.update, product.update, product.upsert, category.update. Zapisujú cez REST API Magenta podpísané OAuth 1.0a a cez tenký modul Deedspanu. Do databáz cudzích systémov sa zapisuje len do medzitabuliek alebo uložených procedúr.
Výrazy
Každé mapované pole má jeden výraz nad zdrojovým riadkom row. Výrazy sa nedostanú k súborom, sieti ani databáze a kontrolujú sa pri načítaní projektu.
| Skupina | Funkcie |
|---|---|
| Text | trim, upper, lower, replace, split, extract, substr, pad, join, regex_match, regex_replace |
| Čísla | int, decimal, round, abs, min, max |
| Dátum | date, format_date, add_days, now |
| Logika | coalesce, empty, in, ? :, ?? |
| Prevody | lookup, xref, setting |
| Kolekcie | map, filter, first, count, sum |
Keď výraz zlyhá, skončí chybou len jeho záznam:
záznam s chybou
map.quantity: int(): "many" is not a whole number. (expression: max(0, int(row['qty'])))
Behy a výsledky
Každé spustenie flow je beh. Delí sa na fázy v RabbitMQ a na dávky najviac po 1 000 záznamov.
- Zápis je idempotentný podľa kľúča, napríklad
TENT-200|main. - Posielajú sa len zmenené záznamy;
compare_with: targetporovnáva s aktuálnym stavom v cieli. - Z jedného flow beží vždy len jeden beh.
- Beh, ktorý by toho zmenil alebo zmazal príliš veľa, počká na schválenie.
| Výsledok | Význam |
|---|---|
written | Odoslané a prijaté |
unchanged | Rovnaké ako minule, neodoslané |
skipped | Vynechal ho hook alebo pravidlo |
failed | Odmietnuté; uloží sa pole a chyba |
deleted | Zmazané, lebo v zdroji už nie je |
Beh skončí ako completed, completed_with_failures alebo failed.
Ako začať
Stačí Docker, PHP beží v kontajneri.
-
Založte projekt
terminál
$ composer create-project deedhold/deedspan-skeleton acme-integrations $ cd acme-integrations $ cp .env.example .env.local # doplňte nastavenia a heslá $ docker compose up -d --build -
Napíšte flow
Začnite s
target: { transport: none }: záznamy sa namapujú a skontrolujú, ale nikam sa nepošlú.flows/stock-from-erp.yaml
flow: stock-from-erp owner: erp record: stock_level source: transport: sftp connection: erp-ftp path: /export/stock_*.csv format: { type: csv, delimiter: ";", encoding: windows-1250 } map: sku: "trim(row['sku'])" location: "lookup('warehouses', row['warehouse'])" quantity: "max(0, int(row['qty']))" target: { transport: none } -
Skontrolujte projekt
terminál
$ bin/console deedspan:flow:validate Record types: category, price, product, stock_level Connectors: magento Flows: stock-from-erp -
Vyskúšajte ho naprázdno
Na cieľ ani databázu nesiahne. Keď niektorý záznam zlyhá, skončí s kódom 1, takže sa hodí do CI.
terminál
$ bin/console deedspan:flow:dry-run stock-from-erp samples/stock.csv TENT-200|main: sku=TENT-200, location=main, quantity=15 BOOT-42|east: sku=BOOT-42, location=east, quantity=3 -
Spustite ho dvakrát
Druhý beh už nič nové nenájde.
terminál
$ bin/console deedspan:flow:run stock-from-erp --sync Run 01a1d5f1 completed: written 2, unchanged 0, skipped 0, failed 0. $ bin/console deedspan:flow:run stock-from-erp --sync Run 01a1d5f9 completed: written 0, unchanged 2, skipped 0, failed 0. -
Posielajte sklad do Magenta podľa plánu
flows/stock-from-erp.yaml
triggers: { schedule: "*/15 * * * *" } target: connector: magento operation: stock.update connection: shop locations: { main: default, east: east } # zdroj MSI pre každé miesto
Príkazový riadok
Každý príkaz najprv načíta celý projekt. Logy idú na stderr ako JSON, výstup na stdout.
| Príkaz | Čo robí |
|---|---|
deedspan:flow:validate | Skontroluje typy záznamov, konektory a flow |
deedspan:flow:dry-run <flow> <sample> | Ukáže, čo by sa zapísalo; --json vypíše celý report |
deedspan:flow:run <flow> | Zaradí beh do frontu; --sync ho vykoná hneď, --force aj napriek bežiacemu behu |
messenger:consume control bulk | Worker |
messenger:consume scheduler_flows | Plánovač (len jeden) |
debug:scheduler | Časy ďalších behov |
messenger:stats | Počet správ vo frontoch |
messenger:failed:show / retry | Správy, ktoré vyčerpali pokusy |
doctrine:migrations:migrate | Tabuľky v databáze; po každom upgrade |
Tým istým nástrojom sa dajú spravovať pripojenia, nasadzovať a vracať verzie flow, znova odosielať chyby, schvaľovať pozastavené behy a exportovať celú inštaláciu. To isté vie API pre vaše nástroje aj portál Deedholdu.
Prevádzka
- Nasadenie
- Docker Compose na linuxovom serveri. CI skontroluje a vyskúša flow, zostaví image a nasadí ho. Prístup k plateným doplnkom ide ako build secret.
- Prostredia
- Najprv staging, produkcia dostane ten istý image po výslovnom schválení.
- Verzie
- Každé nasadenie uloží verziu flow, vrátiť ju ide jedným príkazom. Upgrade jadra:
composer update deedhold/deedspan. - Heslá
- Šifrovane v databáze inštalácie; nikdy v gite, hooky k nim nemajú prístup.
- Logy
- JSON na stderr s behom, flow, verziou, fázou, dávkou a kľúčom záznamu.
- Upozornenia
- Podpísané webhooky a e-mail:
run.failed,record.failed,flow.silent,approval.neededa ďalšie. - Telemetria
- OpenTelemetry cez OTLP, heartbeat URL pre každý flow, Sentry SDK.
- Administrácia Magenta
- Stĺpec Integrácia v prehľade objednávok; pri neúspešnej objednávke chyba a tlačidlo na opakovanie.
- Chyby
- Chybný riadok zhodí len svoj záznam. Neúspešné správy sa skúsia znova trikrát (po 1, 3 a 9 s). Neúspešné záznamy čakajú na zozname, odkiaľ ich pošlete znova.
- Uchovávanie
- Dáta a súbory 30 dní šifrovane, potom história a výsledky.
- Odchod
- Export pridá k repozitáru zálohu databázy a archív; ten istý Compose súbor spustíte kdekoľvek.
stderr
{"level_name":"WARNING","message":"Record failed.","context":{"event":"record.failed"},
"extra":{"run_id":"01a1d668-1076-746d-9a19","flow":"stock-from-erp","stage":"send","chunk":3,"record_key":"STOVE-9|east"}}
Rozšírenia
Najprv YAML a výrazy, PHP len tam, kde nestačia. Inštalácia patrí jednému obchodníkovi, takže PHP z projektu beží bez sandboxu.
Vlastné konektory
Manifest connector.yaml nad všeobecnými prenosmi, PHP len v pomenovaných miestach. Konektor je v priečinku connectors/, alebo ako balík pre Composer.
connectors/trailhub/connector.yaml
schema_version: 1
connector: trailhub
version: 1.0.0
connection:
url: { type: string }
token: { type: string, secret: true }
api:
base_url: "{{ connection.url }}/api"
auth: { type: bearer, token: "{{ connection.token }}" }
operations:
claim.create:
method: POST
path: /claims
class: App\Connector\CreateClaim
record: warranty_claim
Trieda operácie dostane prihláseného klienta API, nikdy samotné heslo, a vracia výsledok pre každý záznam:
RecordOperation
interface RecordOperation
{
public function checkOptions(array $options): void;
/** @return list<RecordResult> one per record, in the same order */
public function send(array $records, OperationContext $context): array;
}
Voliteľne: ComparesWithTarget pre compare_with: target, #[AsReferenceIndex] na kontrolu odkazov, #[AsTransport] a #[AsFormat] pre nové prenosy a formáty.
PHP hooky
Hook dostane jeden záznam a vráti ho, vráti niekoľko záznamov, alebo null, keď sa má preskočiť. K dispozícii má nastavenia, prevodové tabuľky, tabuľku krížových odkazov a logger, nikdy nie pripojenie.
src/Hook/SkipZeroStock.php
final class SkipZeroStock implements Hook
{
public function __invoke(Record $record, HookContext $context): ?Record
{
return '0' === $record->get('quantity') ? null : $record;
}
}
Vo flow ho zapíšete ako hooks: { before_send: App\Hook\SkipZeroStock }.
Vlastné typy záznamov
Behy, opakovania, porovnanie zmien aj skúšky naprázdno fungujú pre vlastné typy rovnako.
records/warranty_claim.yaml
schema_version: 1
record: warranty_claim
version: 1.0.0
key: [claim_no]
fields:
claim_no: { type: string, required: true }
order_no: { type: string, required: true }
sku: { type: string, references: product }
created_at: { type: date }
Typy polí: string, decimal, int, bool, date, enum, object, list.
Správa od Deedholdu
Integrácie vám nastavíme a staging aj produkciu prevádzkujeme za pevný mesačný poplatok za každý napojený systém. Kód zostáva vám.