顧客管理プラグイン (Customer Management Plugin)¶
概要¶
Ritsubiの複雑な顧客管理要件に対応するVendureカスタムプラグイン。動的な顧客ステータス管理と商品アクセス制御を実現します。
親子アカウント(請求先コード / 得意先コード)の実装状況¶
- 親子判定は
billingCustomerCode(請求先コード)を基準に行い、customerCodeはSMILEで発番された得意先コードとして扱う。親(請求先)の場合はbillingCustomerCodeがcustomerCodeと同一値になることを前提とする。Customer.customFields のcustomerCode/billingCustomerCode/roleType(HEAD/BRANCH/STAFF) /branchType(本店/支店/通常)で表現する。 - Order.customFields に
billingCustomerCodeを追加し、請求単位を注文に保持できる。 - Shop API は
CustomerExtensionsPluginでcustomerHierarchyを提供し、ログイン中の顧客を起点に親子情報を返す。 - Vendure Dashboard には請求先コードの手動紐付け・編集UIを設けない。請求先コードはSMILEを正本とし、修正はSMILE側で実施する。
- マイグレーション
20251217093000_replace_account_with_billing_code.tsで Account エンティティ方式からbillingCustomerCode方式へ移行済み。
互換性レンジ¶
- Vendure: 3.5.x(apps/vendure-server は ^3.5.2 を使用)
- Node.js: >=24.7.0(apps/vendure-server/package.json の engines に準拠)
パッケージ情報¶
- パッケージ名:
@ritsubi/customer-management-plugin - パス:
/packages/plugins/customer-management - エントリーポイント:
src/index.ts
実装機能¶
1. 顧客ステータス管理¶
顧客ステータスは CustomerStatusEntity として DB テーブルに管理される動的エンティティです。ステータス数は固定ではなく、CustomerStatusService.initializeDefaultStatuses() で初期値が設定されます。CUSTOMER_STATUS(@ritsubi/domain)に定義された運用定数が主なステータスコードです。
顧客への割り当ては assignStatusToCustomer / removeStatusFromCustomer / bulkUpdateCustomerStatuses で行います。
2. 親子アカウント(顧客階層)¶
親子判定は billingCustomerCode(請求先コード)を基準に行い、customerCode はSMILEで発番された得意先コードとして扱います。CustomerHierarchyService が階層解決ロジックを提供し、Shop API の customerHierarchy クエリとして公開されます。
3. 顧客認証補助¶
CustomerCredentialService / CustomerPasswordAdminService が管理者によるパスワード設定・更新をサポートします。
技術仕様¶
プラグイン設定オプション¶
export interface CustomerManagementPluginOptions {
/**
* 階層的顧客ステータス管理を有効にする
*/
enableStatusHierarchy?: boolean;
/**
* カテゴリアクセス制御を有効にする
*/
enableCategoryAccess?: boolean;
/**
* 管理画面での一括ステータス更新を有効にする
*/
enableBulkStatusUpdate?: boolean;
/**
* ステータス変更履歴を記録する
*/
enableStatusHistory?: boolean;
}
プラグイン初期化¶
import { CustomerManagementPlugin } from "@ritsubi/customer-management-plugin";
// Vendure設定
export const config: VendureConfig = {
plugins: [
CustomerManagementPlugin.init({
enableStatusHierarchy: true,
enableCategoryAccess: true,
enableBulkStatusUpdate: true,
enableStatusHistory: true,
}),
],
};
サービス¶
CustomerStatusService¶
顧客ステータスの管理と検証を行うコアサービス。
主要メソッド:
getCustomerStatuses(ctx, customerId): 顧客のステータス一覧を取得getCustomerPrimaryStatus(ctx, customerId): 顧客のプライマリステータスを取得updateCustomerStatus(ctx, id, input): ステータス情報を更新assignStatusToCustomer(ctx, customerId, statusCode): ステータスを割り当てremoveStatusFromCustomer(ctx, customerId, statusCode): ステータスを解除bulkUpdateCustomerStatuses(ctx, customerIds, statusCode): 複数顧客への一括適用
対応する要件¶
要件定義書との対応¶
- 3.1.1 顧客ステータス管理: CustomerStatusEntity による動的ステータス管理と階層的権限管理
- 3.1.2 商品カテゴリ体系・アクセス制御: 顧客ステータスに応じたカタログ制御
使用例¶
Storefront での顧客階層取得¶
import { useQuery } from "@tanstack/react-query";
import { graphql } from "@/__generated__/gql";
import { vendureQueryClient } from "@/lib/vendure-fetch-client";
const GET_CUSTOMER_HIERARCHY = graphql(`
query GetCustomerHierarchy {
customerHierarchy {
parent {
id
firstName
lastName
customFields {
customerCode
billingCustomerCode
roleType
branchType
}
}
self {
id
firstName
lastName
customFields {
customerCode
billingCustomerCode
roleType
branchType
}
}
children {
id
firstName
lastName
}
}
}
`);
const { data } = useQuery({
queryKey: ["customerHierarchy"],
queryFn: () => vendureQueryClient(GET_CUSTOMER_HIERARCHY, {}),
});
データモデル¶
カスタムフィールド¶
このプラグインが利用する主要な Customer カスタムフィールド(customer-custom-fields.ts で定義):
Customer.customFields.customerCode- SMILE 得意先コードCustomer.customFields.billingCustomerCode- 請求先(親)得意先コードCustomer.customFields.roleType- HEAD / BRANCH / STAFFCustomer.customFields.branchType- 本店 / 支店 / 通常Customer.customFields.directShippingBlocked-trueの場合は直送出荷を禁止(opt-out フラグ)Customer.customFields.allowedShippingModes- 許可された出荷モード一覧
開発ガイド¶
ローカル開発¶
# プラグインディレクトリへ移動
cd packages/plugins/customer-management
# 依存関係インストール
pnpm install
# ビルド
pnpm build
# テスト実行
pnpm test
型定義¶
// CustomerStatusEntity(DBテーブル管理、動的)
export interface CustomerStatus {
code: string;
name: string;
accessLevel: number;
canViewPrices: boolean;
canPurchase: boolean;
}
セキュリティ考慮事項¶
- アクセス制御の徹底: すべてのカテゴリアクセスはバックエンドで検証
- ステータス変更権限: ステータス変更は管理者権限が必要
- 履歴の改ざん防止: ステータス変更履歴は追記のみ可能
- GraphQL認証: すべてのクエリ・ミューテーションで認証を必須化
パフォーマンス最適化¶
- キャッシング: 顧客ステータスとカテゴリアクセス情報をRedisにキャッシュ
- バッチ処理: 一括ステータス更新はトランザクションで処理
- 遅延ロード: カテゴリ一覧は必要に応じて動的にロード
トラブルシューティング¶
よくある問題¶
問題: 顧客がカテゴリにアクセスできない
- 原因: ステータスの権限レベルが不足
- 解決: 管理画面で適切なステータスに更新
問題: ステータス変更が反映されない
- 原因: キャッシュのTTLが長すぎる
- 解決: キャッシュをクリアまたはTTLを調整
CustomerGroup の auto / manual 並存¶
CustomerGroup には手動でメンバを管理する従来運用に加え、auto group(述語ベース)が利用できる。auto group は CustomerGroup.customFields.isAuto = true を持ち、predicate JSON で指定した条件式(Customer custom field の allowlist + equals|in|not_in|exists|not_exists)により、Customer 保存時に物理メンバが自動更新される。SMILE import 経由の更新も CustomerEvent を通じて同じ同期パスを通る。auto group は手動メンバ編集が禁止され、表示制御や商流ルールからは通常の CustomerGroup ID として参照可能。詳細は docs/specifications/2026-05-predicate-based-customer-group.md。
関連ドキュメント¶
- 述語ベース CustomerGroup 仕様 - auto group の SSOT
- 商流ルール - 顧客ステータスに基づく価格・特典制御
- 商流ルール移行メモ - 旧価格/キャンペーン実装の統合経緯
- Vendure機能リファレンス
今後の拡張予定¶
- ステータス自動昇格機能(購入実績ベース)
- カテゴリアクセス申請ワークフロー
- ステータス期限管理(年間契約など)
- 詳細な権限カスタマイズUI