shoplazza/shoplazza-cli

shoplazza-customers

- Use when the user wants to manage customers (buyers) on a shoplazza store through the CLI — create a customer profile, find a customer by email or phone, get customer details or the total customer count, update names / tags / email or SMS marketing subscr…

ソースを見る
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

shoplazza CLI — customers module

CRITICAL — before anything else, use Read on [`../shoplazza-common/SKILL.md`](../shoplazza-common/SKILL.md). It owns every cross-cutting mechanic: the three access tiers, the output envelope (.data), --dry-run, --jq (incl. "don't pass -r"), --fields, schema, api rest, auth / profiles, and the safety protocol. This file covers only the customers domain and never repeats them.

Overview

The customers module manages customer (buyer) profiles and their shipping addresses. All three access tiers apply (tier-selection rule is in shoplazza-common — prefer +shortcut).

Shortcuts are `+create` and `+search` only. There is no `+count` — counting is the count spec leaf — and no +update / +delete; updates are the update leaf, and this module has no customer-delete command at all (only addresses have delete).

Command map

Intent → command, highest-fit tier first. The authoritative flags/params live in shoplazza customers <cmd> --help and shoplazza schema customers.<cmd>, not this table.

User intentCommand
Create a customer`customers +create (--email <e> \--phone <p>) [--first-name --last-name --tags --no-marketing]`
Find customer by email / phonecustomers +search --email <e> / customers +search --phone <p>
Total customer countcustomers count (leaf — there is NO +count)
Get one customer by IDcustomers get --params '{"customer_id":"<id>"}'
List / filter customers (cursor paging, ids, contact)customers list --params '{…}'
Update name / tags / marketing subscriptioncustomers update --params '{"customer_id":"<id>"}' --data '{"customer":{…}}'
List a customer's addressescustomers addresses list --params '{"customer_id":"<id>"}'
Add an addresscustomers addresses create --params '{"customer_id":"<id>"}' --data '{"address":{…}}'
Get / update / delete one addresscustomers addresses {get,update,delete} --params '{"customer_id":"<cid>","address_id":"<aid>"}'
Set the default shipping addresscustomers addresses set-default --params '{"customer_id":"<cid>","address_id":"<aid>"}' (no --data)

Acting on a request

"新建客户" / "add a new customer" / "给客户打标签" / "unsubscribe them" is an action, not a question:

  1. Match intent to a command via the trigger table.
  2. Check the required, no-default values against the user's own words. Missing or ambiguous

ask with `AskUserQuestion`, never fabricate — above all the contact identifier: never invent an email address or phone number.

  1. Never ask about anything with a CLI default (marketing defaults to subscribe; names and

tags are optional). Let the default win.

  1. All required present → execute. Writes here are non-destructive single-record updates;

--dry-run first only if the user asks (see shoplazza-common → Safety protocol).

Trigger phrase → command

User saysCommandHow to extract values
新建客户 / 建个客户档案 / add a new customer+createexactly ONE of --email / --phone; a full name like "Jane Doe" splits into --first-name Jane --last-name Doe
不要订阅营销 / 不订阅邮件 / don't subscribe to marketing+create … --no-marketingdefault is subscribe — the flag MUST be set on explicit opt-out wording
按邮箱查客户 / find the customer by email+search --email <e>the ask is the customer record, not their orders
按手机号查客户 / look up by phone+search --phone <p>do not invent --since/--until time bounds
一共多少客户 / 注册客户数 / how many customerscountleaf, no flags; extract with --jq '.data.count'
给客户 X 打标签 / tag customer Xupdatetags is a WHOLE-ARRAY replace — read-merge-write (see Gotchas)
退订邮件营销 / unsubscribe email marketingupdate with {"customer":{"accepts_marketing":false}}email only — do NOT touch accepts_sms_marketing
退订短信营销 / unsubscribe SMS marketingupdate with {"customer":{"accepts_sms_marketing":false}}SMS only — do NOT touch accepts_marketing
加一个收货地址 / add a shipping addressaddresses createbody requires country + country_code
把地址设为默认 / set the default addressaddresses set-defaultboth IDs are path params via --params; no body

Required-vs-ask matrix

Only no-default values are askable.

CommandMust ASK if unspecifiedInfer if possibleDefault silently
+createthe contact identifier — exactly one of --email / --phone (if you ask, offer both and explain only ONE is needed)--first-name/--last-name from a stated name; --no-marketing from opt-out wording; --tags if the user names tagsmarketing (default subscribe), --tags (omit), names (optional — never ask)
+searchwhich customer — an email or phone to search for, when the user wants a specific person but gave neitherfilter flag from what they gave (email vs phone)--page-limit, --since, --until (all omit)
updatecustomer_id; the new value if wording gave nonewhich field from wording (标签→tags, 邮件营销→accepts_marketing, 短信→accepts_sms_marketing)every field the user did not mention — never send it
addresses createcustomer_id; country + country_code (required body fields)other address fields from wordingdefault and all optional fields (omit)
addresses set-defaultcustomer_id + address_id— (no body at all)

Never-ask list

Optional or defaulted — never a question: --first-name / --last-name / --tags (+create, optional) · marketing subscription (+create defaults to subscribe; set --no-marketing only on explicit opt-out wording, never ask) · --page-limit / --since / --until (+search; omit — never invent time bounds) · optional address fields incl. default.

Boundaries

Reads like customers, actually belongs elsewhere (and vice versa):

Sounds like customersActually belongs toCommand
该客户下过的订单 / order history for jane@example.comshoplazza-ordersan orders search filtered by that customer
按邮箱找到这个客户 / customer profile for an emailthis module (not orders)customers +search --email <e>

Direction decides: 资料 (profile/record) → customers; 订单 (orders placed) → orders. Resolving a customer ID here as an intermediate step for an orders query is fine — the final answer must come from the owning domain.

Permissions · Scope

Authorization is by domain (the unit of --domain), not per command. Authorization flow is in shoplazza-common → Authentication.

OperationNeedsGrant
read (get / list / count / +search / `addresses get\list`)customers read scopeauth login --domain customers
write (+create / create / update / `addresses create\update\delete\set-default`)customers write scopeauth login --domain customers

Look up exact scope literals with shoplazza auth scopes; --domain customers expands into them.

Gotchas

Domain-specific pitfalls only (generic ones — .data prefix, --jq without -r — are in shoplazza-common).

SymptomCauseFix
customers +count → unknown commandShortcuts are +create/+search only (unlike products/orders, customers has no +count)customers count (leaf), extract with --jq '.data.count' — never list-and-count manually
+create rejected: "exactly one of --email or --phone is required"Strict XOR — both given, or neitherPass exactly one; if the user gave neither, ASK — never fabricate contact info
Spec-leaf create fails without contact_typeThe leaf body requires contact_type; +create fills it automatically (email / phone to match the flag)Prefer +create; on the leaf, set "contact_type" to match the identifier you send
Customer's old tags vanished after taggingupdate tags REPLACES the whole arrayRead-merge-write: get current tags, append, then update with the merged array
Unsubscribed more than the user askedEmail (accepts_marketing) and SMS (accepts_sms_marketing) are separate booleansChange only the one named; leave the other out of the body
Tried to change email / phone via updateThe update body only accepts first_name / last_name / accepts_marketing / accepts_sms_marketing / tagsNot expressible via update — check schema customers.update
Leaf list ignores a phone paramlist documents email and contact query params, not phonePrefer +search --phone (it sends contact); on the leaf use "contact" (matches the customer's primary contact — email or phone)
unknown flag: --fields on customers +searchThe customers list endpoint documents no fields param, so the flag was removed rather than left as a silent no-opProject with --jq
Wanted to delete a customerNo customer-delete command exists in this module (only addresses delete)Not expressible at tiers 1–2; confirm an endpoint exists before reaching for api rest
Set default via addresses update with "default":trueA dedicated endpoint owns thisaddresses set-default --params '{"customer_id":…,"address_id":…}' — path params only, no --data
+search --since/--until returns "wrong" customersThey map to created_at_min/created_at_max — bounds on the customer's CREATION timeOnly pass them when the user asks for a signup-time window

Recipes

bash
# 1. New customer with email, opted out of marketing emails
customers +create --email jane.doe@example.com --first-name Jane --last-name Doe --no-marketing

# 2. Find a customer by phone (project with --jq; +search has no --fields)
customers +search --phone 13800138000 --jq '.data.customers[] | {id, name, email, phone}'

# 3. Total registered customers
customers count --jq '.data.count'

# 4. Tag customer 424242 as VIP without clobbering existing tags (read-merge-write)
customers get --params '{"customer_id":"424242"}' --jq '.data.customer.tags'
# → e.g. ["wholesale"]; append and write the MERGED array back:
customers update --params '{"customer_id":"424242"}' --data '{"customer":{"tags":["wholesale","VIP"]}}'

# 5. Unsubscribe email marketing only (SMS untouched)
customers update --params '{"customer_id":"887766"}' --data '{"customer":{"accepts_marketing":false}}'

# 6. Set the default shipping address
customers addresses set-default --params '{"customer_id":"335577","address_id":"ADDR99"}'

# 7. List a customer's address book
customers addresses list --params '{"customer_id":"335577"}' --jq '.data.addresses[] | {id, name, address1, default}'

References

  • Per-command flags: shoplazza customers <cmd> --help
  • Spec-leaf params / body / response: shoplazza schema customers.<cmd> (addresses: schema customers.addresses.<cmd>)
  • Cross-cutting mechanics (auth / tiers / output envelope / --dry-run / --jq / safety): ../shoplazza-common/SKILL.md
  • Shortcut source of truth: shortcuts/customers/*.go