×
¥
查看详情
🔥 会员专享 文生代码 开发

类属性方法生成器

👁️ 484 次查看
📅 Nov 19, 2025
💡 核心价值: 本提示词可根据用户输入生成完整类结构,包括属性、方法及可选构造函数与设计模式,实现面向对象高效建模,适用于前端组件化开发和快速类设计。

🎯 可自定义参数(5个)

编程语言
生成代码所使用的编程语言
类名称
生成的类名称
类表示概念
该类所代表的业务或抽象概念描述
属性信息
类的属性信息,包含属性名称及描述
方法信息
类的方法信息,包含方法名称及描述

🎨 效果示例

// TypeScript: FormFieldValidator // 用于前端表单单字段校验的可组合类,支持同步/异步规则、错误消息国际化、防抖与可观察订阅。 // 规则采用策略模式(每条规则为封装有 test 的策略对象),并提供预设工厂方法。

export type ValidateState = { valid: boolean; errors: string[] }; export type ObserverFn = (state: { valid: boolean; errors: string[]; value: any }) => void;

export type SyncRule = { name: string; test: (v: any) => boolean | string; // 返回 true 通过;false 或 string 失败(string 作为错误码或消息) };

export type AsyncRule = { name: string; test: (v: any) => Promise<boolean | string>; // 返回 true 通过;false 或 string 失败 };

export class FormFieldValidator { fieldName: string; value: any; rules: SyncRule[] = []; asyncRules: AsyncRule[] = []; errors: string[] = []; touched = false; debounceMs = 300; messages: Record<string, string> = {}; observers: Set = new Set();

private _debounceTimer: any = null; private _pendingAsyncId = 0;

constructor( fieldName: string, options?: { value?: any; debounceMs?: number; messages?: Record<string, string>; rules?: SyncRule[]; asyncRules?: AsyncRule[]; touched?: boolean; } ) { this.fieldName = fieldName; this.value = options?.value; if (typeof options?.debounceMs === 'number') this.debounceMs = options.debounceMs; if (options?.messages) this.messages = { ...options.messages }; if (options?.rules) this.rules = [...options.rules]; if (options?.asyncRules) this.asyncRules = [...options.asyncRules]; if (typeof options?.touched === 'boolean') this.touched = options.touched; }

// 执行同步规则(按序短路),更新 errors 并通知观察者 validateSync(): ValidateState { this.touched = true; const err = this.runSyncRules(); this.errors = err ? [err] : []; this.emit(); return { valid: this.errors.length === 0, errors: [...this.errors] }; }

// 执行异步规则(与同步结果合并),支持 trigger 控制防抖与短路 // - change: 应用防抖(默认 debounceMs) // - blur: 立即校验(无防抖) async validateAsync(trigger?: 'change' | 'blur'): Promise { this.touched = true;

// 先跑一次同步,若失败则短路整个异步流程
const syncRes = this.validateSync();
if (!syncRes.valid) {
  // 同步失败直接返回
  return { valid: false, errors: [...this.errors] };
}

// 同步通过再进行异步(按序短路)
const waitMs = trigger === 'blur' ? 0 : this.debounceMs;
const myId = ++this._pendingAsyncId;
if (this._debounceTimer) clearTimeout(this._debounceTimer);

return new Promise<ValidateState>((resolve) => {
  this._debounceTimer = setTimeout(async () => {
    // 若有新的请求发起,则取消本次执行结果
    if (myId !== this._pendingAsyncId) return;

    const err = await this.runAsyncRulesShortCircuit();
    if (myId !== this._pendingAsyncId) return;

    this.errors = err ? [err] : [];
    this.emit();
    resolve({ valid: this.errors.length === 0, errors: [...this.errors] });
  }, waitMs);
});

}

// 设置值,可选触发校验 setValue(val: any, options?: { validate?: boolean }): void { this.value = val; if (options?.validate) { // 常见在 change 上触发:先同步再异步(带防抖) this.validateSync(); // 异步校验不阻塞 this.validateAsync('change').catch(() => void 0); } else { // 不校验,仅通知值变更 this.emit(); } }

// 追加同步规则 addRule(rule: SyncRule): void { // 若同名存在,先移除保证唯一 this.rules = this.rules.filter((r) => r.name !== rule.name); this.rules.push(rule); }

// 追加异步规则 addAsyncRule(rule: AsyncRule): void { this.asyncRules = this.asyncRules.filter((r) => r.name !== rule.name); this.asyncRules.push(rule); }

// 按名称移除(同步与异步) removeRule(name: string): void { this.rules = this.rules.filter((r) => r.name !== name); this.asyncRules = this.asyncRules.filter((r) => r.name !== name); }

// 设置/覆盖错误文案(国际化) setMessages(map: Record<string, string>): void { this.messages = { ...this.messages, ...map }; }

// 重置状态 reset(): void { this.value = undefined; this.errors = []; this.touched = false; if (this._debounceTimer) { clearTimeout(this._debounceTimer); this._debounceTimer = null; } this._pendingAsyncId++; // 使任何在途异步结果失效 this.emit(); }

// 订阅状态变化 subscribe(fn: ObserverFn): () => void { this.observers.add(fn); return () => { this.observers.delete(fn); }; }

// 工厂方法:按预设快速创建 static fromPreset(preset: 'email' | 'mobile' | 'passwordStrong'): FormFieldValidator { switch (preset) { case 'email': { const v = new FormFieldValidator('email', { messages: { required: '{field}为必填项', 'email.invalid': '{field}格式不正确', }, }); // email 仅在非空时校验格式;是否必填由外部添加 required 规则控制 v.addRule({ name: 'email', test: (val: any) => { if (val === undefined || val === null || String(val).trim() === '') return true; const ok = /^[^\s@]+@[^\s@]+.[^\s@]+$/.test(String(val)); return ok || 'email.invalid'; }, }); return v; } case 'mobile': { const v = new FormFieldValidator('mobile', { messages: { required: '{field}为必填项', 'mobile.invalid': '{field}格式不正确', }, }); v.addRule({ name: 'mobile', test: (val: any) => { if (val === undefined || val === null || String(val).trim() === '') return true; const ok = /^1[3-9]\d{9}$/.test(String(val)); return ok || 'mobile.invalid'; }, }); return v; } case 'passwordStrong': { const v = new FormFieldValidator('password', { messages: { required: '{field}为必填项', 'password.minLength': '{field}长度不能少于{min}个字符', 'password.weak': '{field}需要包含至少{kinds}种字符类型(大写、小写、数字、特殊符号)', }, }); // 长度规则,使用占位符:通过“错误码|参数”形式传参,如 "password.minLength|min=8" v.addRule({ name: 'password.minLength', test: (val: any) => { const s = String(val ?? ''); return s.length >= 8 || 'password.minLength|min=8'; }, }); // 至少包含两类字符:大写/小写/数字/特殊 v.addRule({ name: 'password.weak', test: (val: any) => { const s = String(val ?? ''); let kinds = 0; if (/[A-Z]/.test(s)) kinds++; if (/[a-z]/.test(s)) kinds++; if (/\d/.test(s)) kinds++; if (/[^A-Za-z0-9]/.test(s)) kinds++; return kinds >= 2 || 'password.weak|kinds=2'; }, }); return v; } } }

// ===== 内部实现 =====

private emit(): void { const state = { valid: this.errors.length === 0, errors: [...this.errors], value: this.value }; this.observers.forEach((fn) => { try { fn(state); } catch { // 忽略订阅者内部异常,避免影响外部流程 } }); }

private runSyncRules(): string | null { for (const rule of this.rules) { const res = rule.test(this.value); if (res === true) continue; const msg = this.resolveMessage(res, rule.name); return msg ?? rule.name; } return null; }

private async runAsyncRulesShortCircuit(): Promise<string | null> { for (const rule of this.asyncRules) { const res = await rule.test(this.value); if (res === true) continue; const msg = this.resolveMessage(res, rule.name); return msg ?? rule.name; } return null; }

// 将规则返回结果转换为最终的消息文本 // - true => null(通过) // - false => 使用规则名作为错误码 // - string => 作为“错误码或直出消息”。如果包含“|k=v”参数,视为错误码+占位符参数 private resolveMessage(result: boolean | string, ruleName: string): string | null { if (result === true) return null;

// 将 false 转换为规则名(作为错误码)
const token = result === false ? ruleName : String(result);

const parsed = this.parseErrorToken(token);
const template = this.messages[parsed.code];

if (template) {
  return this.formatWithParams(template, parsed.params);
}

// 若找不到模板,则将 code 当作消息文本直出(忽略参数部分)
return parsed.code;

}

// 解析“错误码|k1=v1&k2=v2”形式的 token private parseErrorToken(token: string): { code: string; params: Record<string, string> } { const [code, rawParams] = token.split('|', 2); const params: Record<string, string> = {}; if (rawParams) { // 支持 &、, 或 ; 分隔 rawParams.split(/[&,;]+/).forEach((pair) => { const [k, v] = pair.split('='); if (k) params[k.trim()] = decodeURIComponent((v ?? '').trim()); }); } return { code, params }; }

// 用占位符替换:支持 {field} / {value} 以及自定义参数 private formatWithParams(template: string, params?: Record<string, any>): string { const merged = { field: this.fieldName, value: this.value, ...(params || {}), }; return template.replace(/{(\w+)}/g, (_, k) => { const v = merged[k]; return v === null || v === undefined ? '' : String(v); }); } }

// 示例:常用规则可按如下方式外部添加 // const v = new FormFieldValidator('username', { messages: { required: '{field}为必填项' } }); // v.addRule({ name: 'required', test: v => (v != null && String(v).trim() !== '') || 'required' }); // v.addAsyncRule({ name: 'unique', test: async v => (await apiCheckUnique(v)) || 'username.taken' }); // v.setMessages({ 'username.taken': '{field}已被占用' });

// SPDX-License-Identifier: MIT package com.example.ecommerce.cart;

import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.time.Clock; import java.time.Instant; import java.util.*;

/**

  • 购物车聚合根:管理购物项、优惠与结算,保证金额精度与货币一致性。

    • 同一货币约束
    • 策略模式:可插拔折扣与运费策略
    • 构建者:可扩展促销规则与外部服务 */ public final class ShoppingCart {

    // 基础配置 private final Currency currency; // 购物车货币(单一) private final MathContext mathContext; // 金额计算精度 private final RoundingMode roundingMode; // 金额舍入规则 private final int moneyScale; // 金额小数位(通常 2) private final Clock clock; // 可注入时钟,便于测试

    // 聚合内数据 private final List items; // 购物项集合 private final List discountStrategies; // 折扣策略链 private ShippingPolicy shippingPolicy; // 运费策略 private final Set couponCodes; // 已应用优惠券(去重) private BigDecimal subtotalCache; // 小计缓存(派生) private boolean subtotalDirty; // 缓存失效标记

    private Instant updatedAt; // 最近更新时间 private String idempotencyKey; // 幂等键(结算/下单防重)

    // 外部协作者(策略/服务) private final InventoryPolicy inventoryPolicy; // 库存/最小购买校验策略 private final CouponValidator couponValidator; // 优惠券校验器 private final PaymentPreparation paymentPreparation; // 支付前准备(可与支付网关协作)

    // ======================= 构造与构建者 =======================

    private ShoppingCart(Builder b) { Objects.requireNonNull(b.currency, "currency required"); this.currency = b.currency; this.mathContext = b.mathContext != null ? b.mathContext : MathContext.DECIMAL64; this.roundingMode = b.roundingMode != null ? b.roundingMode : RoundingMode.HALF_UP; this.moneyScale = b.moneyScale > 0 ? b.moneyScale : 2; this.clock = b.clock != null ? b.clock : Clock.systemUTC();

     this.items = new ArrayList<>();
     if (b.initialItems != null) {
         for (CartItem it : b.initialItems) {
             // 防止不同货币:单价不含币种,视为与购物车币种一致
             addItem(it.getSkuId(), it.getTitle(), it.getUnitPrice(), it.getQty(), it.getAttrs());
         }
     }
    
     this.discountStrategies = new ArrayList<>(b.discountStrategies != null ? b.discountStrategies : Collections.emptyList());
     this.shippingPolicy = Objects.requireNonNullElseGet(b.shippingPolicy, () -> new FreeShippingThresholdPolicy(Money.zero(currency), Money.zero(currency)));
     this.couponCodes = new LinkedHashSet<>(b.initialCoupons != null ? b.initialCoupons : Collections.emptySet());
     this.inventoryPolicy = Objects.requireNonNullElseGet(b.inventoryPolicy, NoopInventoryPolicy::new);
     this.couponValidator = Objects.requireNonNullElseGet(b.couponValidator, () -> CouponValidationResult.invalid("no validator"));
     this.paymentPreparation = Objects.requireNonNullElseGet(b.paymentPreparation, NoopPaymentPreparation::new);
    
     this.subtotalCache = BigDecimal.ZERO.setScale(moneyScale, roundingMode);
     this.subtotalDirty = true;
     this.updatedAt = clock.instant();
     this.idempotencyKey = b.idempotencyKey;
    

    }

    public static Builder builder() { return new Builder(); }

    // ======================= 聚合根操作 =======================

    /**

    • 若已存在同款(skuId + attrs 相同)则合并数量;价格以最新为准。 */ public synchronized void addItem(String skuId, String title, BigDecimal unitPrice, int qty, Map<String, String> attrs) { requireNonBlank(skuId, "skuId"); requireNonBlank(title, "title"); Objects.requireNonNull(unitPrice, "unitPrice"); if (qty <= 0) throw new IllegalArgumentException("qty must be > 0"); Map<String, String> safeAttrs = attrs == null ? Collections.emptyMap() : new TreeMap<>(attrs);

      BigDecimal normalizedPrice = normalizeAmount(unitPrice); Optional existing = findItem(skuId, safeAttrs);

      int newQty = qty; if (existing.isPresent()) { CartItem item = existing.get(); newQty = Math.addExact(item.getQty(), qty); // 库存与最小购买校验 validateQtyRules(skuId, safeAttrs, newQty); // 合并:更新数量与价格 item.setQty(newQty); item.setUnitPrice(normalizedPrice); } else { // 校验 validateQtyRules(skuId, safeAttrs, qty); items.add(new CartItem(skuId, title, normalizedPrice, qty, safeAttrs)); }

      markDirty(); }

    /**

    • 按 SKU 移除购物项(移除该 SKU 的所有变体)。 */ public synchronized void removeItem(String skuId) { requireNonBlank(skuId, "skuId"); boolean removed = items.removeIf(i -> i.getSkuId().equals(skuId)); if (removed) { markDirty(); } }

    /**

    • 调整指定 SKU 的数量(如果存在多个变体,全部调整为该数量;如需变体级别,可扩展重载)。
    • qty <= 0 则移除该 SKU。 */ public synchronized void changeQty(String skuId, int qty) { requireNonBlank(skuId, "skuId"); if (qty <= 0) { removeItem(skuId); return; } boolean any = false; for (CartItem it : items) { if (it.getSkuId().equals(skuId)) { validateQtyRules(it.getSkuId(), it.getAttrs(), qty); it.setQty(qty); any = true; } } if (!any) { throw new NoSuchElementException("SKU not in cart: " + skuId); } markDirty(); }

    /**

    • 应用优惠券:验证有效期、可叠加性与适用范围后记录。
    • 返回是否成功应用(重复添加返回 false)。 */ public synchronized boolean applyCoupon(String code) { requireNonBlank(code, "coupon code"); if (couponCodes.contains(code)) { return false; // 去重 } CouponValidationResult result = couponValidator.validate(code, this, Collections.unmodifiableSet(couponCodes)); if (!result.isValid()) { return false; } // 可叠加性由策略链在计算折扣时综合判断;此处仅记录 couponCodes.add(code); touch(); return true; }

    /**

    • 计算应付总额:subtotal + shipping(估算) - discount。
    • 注意:如需地址敏感的运费,请使用 calculateTotal(Address)。 */ public synchronized Money calculateTotal() { return calculateTotal(null); }

    /**

    • 计算应付总额(地址可选)。 */ public synchronized Money calculateTotal(Address shippingAddress) { Money subtotal = getSubtotalMoney(); Money shipping = shippingPolicy.estimate(this, Optional.ofNullable(shippingAddress)); Money discount = calculateDiscounts(subtotal, shipping, shippingAddress); Money total = subtotal.add(shipping).subtract(discount); if (total.isNegative()) { total = Money.zero(currency); } return total; }

    /**

    • 结算:生成订单草案、锁定库存并返回应付信息。

      • 幂等键透传至支付网关/准备阶段
      • 返回应付金额、运费、折扣、已用优惠券与库存锁定凭证等 */ public synchronized CheckoutResult checkout(PaymentGateway gateway, Address shippingAddress) { Objects.requireNonNull(gateway, "PaymentGateway required"); Objects.requireNonNull(shippingAddress, "shipping address required");

      if (items.isEmpty()) { throw new IllegalStateException("cart is empty"); }

      // 1) 计算金额 Money subtotal = getSubtotalMoney(); Money shipping = shippingPolicy.estimate(this, Optional.of(shippingAddress)); Money discount = calculateDiscounts(subtotal, shipping, shippingAddress); Money totalPayable = subtotal.add(shipping).subtract(discount); if (totalPayable.isNegative()) totalPayable = Money.zero(currency);

      // 2) 锁定库存 String reservationToken = inventoryPolicy.reserve(items);

      // 3) 准备支付(可用于创建支付意图等) String idem = getOrGenerateIdempotencyKey(); PaymentPreparation.PreparedPayment prepared = paymentPreparation.prepare(totalPayable, idem, shippingAddress); PaymentGateway.PaymentIntent intent = gateway.createPaymentIntent(totalPayable, idem, prepared);

      // 4) 生成 CheckoutResult CheckoutResult result = new CheckoutResult( UUID.randomUUID().toString(), // orderDraftId subtotal, shipping, discount, totalPayable, Collections.unmodifiableList(new ArrayList<>(items)), Collections.unmodifiableSet(new LinkedHashSet<>(couponCodes)), reservationToken, intent.intentId(), intent.raw() );

      touch(); // 更新更新时间 return result; }

    /**

    • 清空购物车与派生缓存。 */ public synchronized void clear() { items.clear(); couponCodes.clear(); subtotalCache = Money.zero(currency).amount(); subtotalDirty = true; touch(); }

    public static Builder builder(Currency currency) { return new Builder().currency(currency); }

    // ======================= 内部计算/校验 =======================

    private Optional findItem(String skuId, Map<String, String> attrs) { for (CartItem it : items) { if (it.getSkuId().equals(skuId) && it.getAttrs().equals(attrs)) { return Optional.of(it); } } return Optional.empty(); }

    private void validateQtyRules(String skuId, Map<String, String> attrs, int qty) { if (qty < 1) throw new IllegalArgumentException("min qty is 1"); int max = inventoryPolicy.getMaxPurchasable(skuId, attrs); if (qty > max) { throw new IllegalArgumentException("qty exceeds available limit: " + max); } }

    private Money calculateDiscounts(Money subtotal, Money shipping, Address shippingAddress) { if (discountStrategies.isEmpty()) return Money.zero(currency);

     DiscountContext ctx = new DiscountContext(
             currency, mathContext, roundingMode, moneyScale,
             Collections.unmodifiableSet(couponCodes),
             subtotal, shipping, Optional.ofNullable(shippingAddress));
    
     Money totalDiscount = Money.zero(currency);
     for (DiscountStrategy strategy : discountStrategies) {
         Discount d = strategy.apply(this, ctx);
         if (d == null || d.amount() == null) continue;
         Money delta = d.amount();
         if (!delta.currency().equals(currency)) {
             throw new IllegalStateException("discount currency mismatch");
         }
         if (delta.isNegative()) continue; // 保护,不支持负折扣
         totalDiscount = totalDiscount.add(delta);
     }
     // 折扣不得超过 subtotal + shipping
     Money cap = subtotal.add(shipping);
     if (totalDiscount.gt(cap)) totalDiscount = cap;
     return totalDiscount;
    

    }

    private Money getSubtotalMoney() { BigDecimal amt = getSubtotalAmount(); return Money.of(amt, currency, moneyScale, roundingMode); }

    private BigDecimal getSubtotalAmount() { if (subtotalDirty) { BigDecimal sum = BigDecimal.ZERO.setScale(moneyScale, roundingMode); for (CartItem it : items) { BigDecimal line = it.getUnitPrice().multiply(BigDecimal.valueOf(it.getQty()), mathContext); line = line.setScale(moneyScale, roundingMode); sum = sum.add(line, mathContext).setScale(moneyScale, roundingMode); } subtotalCache = sum; subtotalDirty = false; } return subtotalCache; }

    private void markDirty() { subtotalDirty = true; touch(); }

    private void touch() { updatedAt = clock.instant(); }

    private BigDecimal normalizeAmount(BigDecimal amount) { return amount.setScale(moneyScale, roundingMode); }

    private static void requireNonBlank(String s, String name) { if (s == null || s.trim().isEmpty()) throw new IllegalArgumentException(name + " required"); }

    private String getOrGenerateIdempotencyKey() { if (idempotencyKey == null || idempotencyKey.isBlank()) { idempotencyKey = "cart-" + UUID.randomUUID(); } return idempotencyKey; }

    // ======================= 对外可读属性(只读视图) =======================

    public Currency getCurrency() { return currency; } public List getItems() { return Collections.unmodifiableList(items); } public List getDiscountStrategies() { return Collections.unmodifiableList(discountStrategies); } public ShippingPolicy getShippingPolicy() { return shippingPolicy; } public Set getCouponCodes() { return Collections.unmodifiableSet(couponCodes); } public BigDecimal getSubtotal() { return getSubtotalAmount(); } public Instant getUpdatedAt() { return updatedAt; } public String getIdempotencyKey() { return idempotencyKey; }

    // 可选:动态更换运费策略(例如结算时基于地址) public synchronized void setShippingPolicy(ShippingPolicy shippingPolicy) { this.shippingPolicy = Objects.requireNonNull(shippingPolicy); touch(); }

    public synchronized void setIdempotencyKey(String idempotencyKey) { this.idempotencyKey = idempotencyKey; touch(); }

    // ======================= 嵌套类型:值对象/策略/接口 =======================

    public static final class CartItem { private final String skuId; private final String title; private BigDecimal unitPrice; // 与购物车货币一致 private int qty; private final Map<String, String> attrs;

     CartItem(String skuId, String title, BigDecimal unitPrice, int qty, Map<String, String> attrs) {
         this.skuId = skuId;
         this.title = title;
         this.unitPrice = unitPrice;
         this.qty = qty;
         this.attrs = Collections.unmodifiableMap(new TreeMap<>(attrs));
     }
    
     public String getSkuId() { return skuId; }
     public String getTitle() { return title; }
     public BigDecimal getUnitPrice() { return unitPrice; }
     public int getQty() { return qty; }
     public Map<String, String> getAttrs() { return attrs; }
    
     void setUnitPrice(BigDecimal unitPrice) {
         this.unitPrice = unitPrice;
     }
     void setQty(int qty) {
         this.qty = qty;
     }
    
     @Override
     public String toString() {
         return "CartItem{" +
                 "skuId='" + skuId + '\'' +
                 ", title='" + title + '\'' +
                 ", unitPrice=" + unitPrice +
                 ", qty=" + qty +
                 ", attrs=" + attrs +
                 '}';
     }
    

    }

    public record Money(BigDecimal amount, Currency currency) { public static Money of(BigDecimal amount, Currency currency, int scale, RoundingMode rm) { return new Money(amount.setScale(scale, rm), currency); } public static Money zero(Currency c) { return new Money(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), c); } public Money add(Money other) { requireSameCurrency(other); return new Money(amount.add(other.amount), currency); } public Money subtract(Money other) { requireSameCurrency(other); return new Money(amount.subtract(other.amount), currency); } public boolean isNegative() { return amount.signum() < 0; } public boolean gt(Money other) { requireSameCurrency(other); return amount.compareTo(other.amount) > 0; } private void requireSameCurrency(Money other) { if (!currency.equals(other.currency)) throw new IllegalArgumentException("currency mismatch"); } public BigDecimal amount() { return amount; } // alias }

    public interface DiscountStrategy { Discount apply(ShoppingCart cart, DiscountContext ctx); }

    public static final class DiscountContext { public final Currency currency; public final MathContext mathContext; public final RoundingMode roundingMode; public final int moneyScale; public final Set couponCodes; public final Money subtotal; public final Money shipping; public final Optional

    shippingAddress;

     public DiscountContext(Currency currency,
                            MathContext mathContext,
                            RoundingMode roundingMode,
                            int moneyScale,
                            Set<String> couponCodes,
                            Money subtotal,
                            Money shipping,
                            Optional<Address> shippingAddress) {
         this.currency = currency;
         this.mathContext = mathContext;
         this.roundingMode = roundingMode;
         this.moneyScale = moneyScale;
         this.couponCodes = couponCodes;
         this.subtotal = subtotal;
         this.shipping = shipping;
         this.shippingAddress = shippingAddress;
     }
    
     public Money money(BigDecimal amt) {
         return Money.of(amt, currency, moneyScale, roundingMode);
     }
    

    }

    public record Discount(Money amount, String reason, Map<String, Object> meta) { public static Discount of(Money amount, String reason) { return new Discount(amount, reason, Collections.emptyMap()); } }

    public interface ShippingPolicy { Money estimate(ShoppingCart cart, Optional

    shippingAddress); }

    /**

    • 示例运费策略:满额包邮,否则固定运费;可根据区域差异化(演示结构)。 */ public static final class FreeShippingThresholdPolicy implements ShippingPolicy { private final Money threshold; private final Money baseFee;

      public FreeShippingThresholdPolicy(Money threshold, Money baseFee) { this.threshold = threshold; this.baseFee = baseFee; }

      @Override public Money estimate(ShoppingCart cart, Optional

      shippingAddress) { Money subtotal = cart.getSubtotalMoney(); if (!subtotal.currency().equals(threshold.currency)) { throw new IllegalStateException("currency mismatch in shipping policy"); } // 区域差异示意:海外加收 50% boolean overseas = shippingAddress.map(a -> !"CN".equalsIgnoreCase(a.country())).orElse(false); if (subtotal.amount().compareTo(threshold.amount) >= 0) { return Money.of(BigDecimal.ZERO, threshold.currency, 2, RoundingMode.HALF_UP); } Money fee = baseFee; if (overseas) { BigDecimal up = fee.amount().multiply(BigDecimal.valueOf(1.5)); fee = Money.of(up, fee.currency(), 2, RoundingMode.HALF_UP); } return fee; } }

    public interface CouponValidator { CouponValidationResult validate(String code, ShoppingCart cart, Set existingCodes); }

    public interface CouponValidationResult extends CouponValidator { boolean isValid(); String reason();

     static CouponValidationResult ok() {
         return new CouponValidationResult() {
             public boolean isValid() { return true; }
             public String reason() { return "OK"; }
             @Override public CouponValidationResult validate(String code, ShoppingCart cart, Set<String> existingCodes) { return this; }
         };
     }
    
     static CouponValidationResult invalid(String reason) {
         return new CouponValidationResult() {
             public boolean isValid() { return false; }
             public String reason() { return reason; }
             @Override public CouponValidationResult validate(String code, ShoppingCart cart, Set<String> existingCodes) { return this; }
         };
     }
    

    }

    public interface InventoryPolicy { int getMaxPurchasable(String skuId, Map<String, String> attrs); String reserve(List items); }

    public static final class NoopInventoryPolicy implements InventoryPolicy { @Override public int getMaxPurchasable(String skuId, Map<String, String> attrs) { return Integer.MAX_VALUE / 4; } @Override public String reserve(List items) { return "noop-reservation-" + UUID.randomUUID(); } }

    public interface PaymentPreparation { PreparedPayment prepare(Money total, String idempotencyKey, Address shippingAddress); record PreparedPayment(Map<String, Object> meta) {} }

    public static final class NoopPaymentPreparation implements PaymentPreparation { @Override public PreparedPayment prepare(Money total, String idempotencyKey, Address shippingAddress) { return new PreparedPayment(Map.of("prepared", true)); } }

    public interface PaymentGateway { PaymentIntent createPaymentIntent(Money amount, String idempotencyKey, PaymentPreparation.PreparedPayment prepared); record PaymentIntent(String intentId, Map<String, Object> raw) {} }

    public record Address(String country, String region, String city, String postcode, String addressLine) {}

    public static final class CheckoutResult { private final String orderDraftId; private final Money subtotal; private final Money shipping; private final Money discount; private final Money totalPayable; private final List lineItems; private final Set appliedCoupons; private final String inventoryReservationToken; private final String paymentIntentId; private final Map<String, Object> paymentRaw;

     public CheckoutResult(String orderDraftId,
                           Money subtotal,
                           Money shipping,
                           Money discount,
                           Money totalPayable,
                           List<CartItem> lineItems,
                           Set<String> appliedCoupons,
                           String inventoryReservationToken,
                           String paymentIntentId,
                           Map<String, Object> paymentRaw) {
         this.orderDraftId = orderDraftId;
         this.subtotal = subtotal;
         this.shipping = shipping;
         this.discount = discount;
         this.totalPayable = totalPayable;
         this.lineItems = lineItems;
         this.appliedCoupons = appliedCoupons;
         this.inventoryReservationToken = inventoryReservationToken;
         this.paymentIntentId = paymentIntentId;
         this.paymentRaw = paymentRaw;
     }
    
     public String orderDraftId() { return orderDraftId; }
     public Money subtotal() { return subtotal; }
     public Money shipping() { return shipping; }
     public Money discount() { return discount; }
     public Money totalPayable() { return totalPayable; }
     public List<CartItem> lineItems() { return lineItems; }
     public Set<String> appliedCoupons() { return appliedCoupons; }
     public String inventoryReservationToken() { return inventoryReservationToken; }
     public String paymentIntentId() { return paymentIntentId; }
     public Map<String, Object> paymentRaw() { return paymentRaw; }
    

    }

    // ======================= 构建者 =======================

    public static final class Builder { private Currency currency; private List initialItems; private List discountStrategies; private ShippingPolicy shippingPolicy; private Set initialCoupons; private InventoryPolicy inventoryPolicy; private CouponValidator couponValidator; private PaymentPreparation paymentPreparation;

     private MathContext mathContext;
     private RoundingMode roundingMode;
     private int moneyScale = 2;
     private Clock clock;
     private String idempotencyKey;
    
     public Builder currency(Currency currency) { this.currency = currency; return this; }
     public Builder items(List<CartItem> items) { this.initialItems = items; return this; }
     public Builder discountStrategies(List<DiscountStrategy> strategies) { this.discountStrategies = strategies; return this; }
     public Builder addDiscountStrategy(DiscountStrategy s) {
         if (this.discountStrategies == null) this.discountStrategies = new ArrayList<>();
         this.discountStrategies.add(s);
         return this;
     }
     public Builder shippingPolicy(ShippingPolicy policy) { this.shippingPolicy = policy; return this; }
     public Builder coupons(Set<String> coupons) { this.initialCoupons = coupons; return this; }
     public Builder inventoryPolicy(InventoryPolicy policy) { this.inventoryPolicy = policy; return this; }
     public Builder couponValidator(CouponValidator validator) { this.couponValidator = validator; return this; }
     public Builder paymentPreparation(PaymentPreparation prep) { this.paymentPreparation = prep; return this; }
    
     public Builder mathContext(MathContext mc) { this.mathContext = mc; return this; }
     public Builder roundingMode(RoundingMode rm) { this.roundingMode = rm; return this; }
     public Builder moneyScale(int scale) { this.moneyScale = scale; return this; }
     public Builder clock(Clock clock) { this.clock = clock; return this; }
     public Builder idempotencyKey(String key) { this.idempotencyKey = key; return this; }
    
     public ShoppingCart build() {
         return new ShoppingCart(this);
     }
    

    }

    // ======================= 示例折扣策略(可选) =======================

    /**

    • 示例:满减策略。满 X 减 Y,不可为负。 */ public static final class FullReductionStrategy implements DiscountStrategy { private final Money threshold; private final Money reduction;

      public FullReductionStrategy(Money threshold, Money reduction) { this.threshold = threshold; this.reduction = reduction; if (!threshold.currency.equals(reduction.currency)) { throw new IllegalArgumentException("currency mismatch in FullReductionStrategy"); } }

      @Override public Discount apply(ShoppingCart cart, DiscountContext ctx) { if (!ctx.subtotal.currency().equals(threshold.currency)) return null; if (ctx.subtotal.amount().compareTo(threshold.amount) >= 0) { return Discount.of(Money.of(reduction.amount, reduction.currency, ctx.moneyScale, ctx.roundingMode), "满减"); } return null; } }

    /**

    • 示例:会员折扣(百分比)。 */ public static final class MemberPercentageOffStrategy implements DiscountStrategy { private final BigDecimal percent; // 0.05 表示 5%

      public MemberPercentageOffStrategy(BigDecimal percent) { if (percent == null || percent.compareTo(BigDecimal.ZERO) <= 0) { throw new IllegalArgumentException("percent must be > 0"); } this.percent = percent; }

      @Override public Discount apply(ShoppingCart cart, DiscountContext ctx) { BigDecimal amt = ctx.subtotal.amount().multiply(percent, ctx.mathContext); amt = amt.setScale(ctx.moneyScale, ctx.roundingMode); if (amt.signum() <= 0) return null; return Discount.of(ctx.money(amt), "会员折扣"); } }

    /**

    • 示例:优惠券策略(基于已应用 couponCodes,简单演示)。 */ public static final class CouponDiscountStrategy implements DiscountStrategy { @Override public Discount apply(ShoppingCart cart, DiscountContext ctx) { if (ctx.couponCodes.isEmpty()) return null; // 简单规则演示:SAVE10 -> 减 10;SAVE20 -> 满 100 减 20;不可为负 BigDecimal total = BigDecimal.ZERO; boolean save10 = ctx.couponCodes.contains("SAVE10"); boolean save20 = ctx.couponCodes.contains("SAVE20"); if (save10) total = total.add(BigDecimal.TEN); if (save20 && ctx.subtotal.amount().compareTo(new BigDecimal("100.00")) >= 0) { total = total.add(new BigDecimal("20.00")); } if (total.signum() <= 0) return null; return Discount.of(ctx.money(total), "优惠券"); } } }

下面给出一个可直接复制使用的 C# 实现,包含 EventBus 类及其所需的配套接口/选项类型。该实现特点:

  • 主题隔离:每个 topic 单独维护订阅者与粘性事件。
  • 同步/异步分发:Publish 可选择同步分发(立即执行)或异步分发(通过 Channel 队列背压)。
  • 中间件链:按注册顺序流水线处理事件(支持鉴权、过滤、埋点等),返回 null 可拦截事件。
  • 订阅控制:一次性订阅(Once)、优先级(Priority)、并发度(MaxConcurrency)、新订阅者是否立即重放粘性事件(ReplayStickyImmediately)。
  • 粘性事件与重放:支持粘性事件存储与 Replay。
  • 事务性发布:PublishOptions 支持传入 EventBusTransaction,将事件缓冲直至 Commit。
  • Flush/Dispose:Flush 等待队列与在途事件清空;Dispose 优雅关闭。

说明:

  • 为方便集成,使用 Microsoft.Extensions.Logging 的 ILogger(可选依赖)。
  • subscribers 的 value 为 List,通过锁保证该 List 的并发安全;字典本身为 ConcurrentDictionary。
  • IEvent 统一为 Envelope,携带 Topic、Payload、类型、时间戳、是否粘性等元信息。

代码如下:

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.Extensions.Logging;

namespace LightweightEventBus { // 事件包裹统一接口 public interface IEvent { string Topic { get; } object Payload { get; } Type PayloadType { get; } DateTimeOffset Timestamp { get; } bool Sticky { get; } // 可扩展元数据 IReadOnlyDictionary<string, object?> Metadata { get; } }

// 订阅处理器接口(供 subscribers 字典保存)
public interface IHandler
{
    int Priority { get; }
    int MaxConcurrency { get; }
    bool Once { get; }
    Type EventType { get; }
    Delegate OriginalDelegate { get; } // 用于 Unsubscribe 匹配
    Task Invoke(IEvent evt, CancellationToken ct);
    SemaphoreSlim Semaphore { get; } // 控制并发度
}

// 订阅参数
public sealed class SubscribeOptions
{
    // 一次性订阅(处理一次后自动取消)
    public bool Once { get; set; } = false;
    // 优先级(数值越大越优先)
    public int Priority { get; set; } = 0;
    // 每个处理器的最大并发度(>1 时允许同一处理器并发处理多个事件)
    public int MaxConcurrency { get; set; } = 1;
    // 订阅建立后是否立即重放粘性事件(若存在)
    public bool ReplayStickyImmediately { get; set; } = true;
}

// 发布参数
public sealed class PublishOptions
{
    // 延迟发布(同步/异步均可生效)
    public TimeSpan? Delay { get; set; }
    // 是否作为粘性事件存储
    public bool Sticky { get; set; } = false;
    // 是否同步分发(true 则在当前调用栈中经过中间件并直接分发到订阅者)
    public bool Synchronous { get; set; } = false;
    // 事务:若提供则事件先缓冲,待 Commit 后统一入队分发
    public EventBus.EventBusTransaction? Transaction { get; set; }
    // 附加元数据(可用于追踪/链路)
    public Dictionary<string, object?>? Metadata { get; set; }
}

// EventBus 实现
public sealed class EventBus : IDisposable
{
    // 必要属性(按需求命名)
    public ConcurrentDictionary<string, List<IHandler>> subscribers { get; }
    public Channel<IEvent> queue { get; }
    public bool enableReplay { get; }
    public Dictionary<string, IEvent> stickyEvents { get; }
    public List<Func<IEvent, Task<IEvent?>>> middlewares { get; }
    public ILogger? logger { get; }

    // 内部状态
    private readonly CancellationTokenSource _cts = new();
    private readonly Task _consumerTask;
    private readonly object _stickyLock = new();
    private volatile bool _disposed;

    // Flush 支持
    private long _enqueued;    // 入队总数(或同步路径计作入处理)
    private long _processed;   // 完成处理的总数
    private TaskCompletionSource<bool> _idleTcs = CreateIdleTcs(completed: true);

    // 构造函数
    public EventBus(
        int capacity = 1024,
        BoundedChannelFullMode fullMode = BoundedChannelFullMode.Wait,
        bool enableReplay = true,
        ILogger? logger = null)
    {
        this.logger = logger;
        this.enableReplay = enableReplay;

        subscribers = new ConcurrentDictionary<string, List<IHandler>>(StringComparer.Ordinal);
        stickyEvents = new Dictionary<string, IEvent>(StringComparer.Ordinal);
        middlewares = new List<Func<IEvent, Task<IEvent?>>>();

        var channel = Channel.CreateBounded<IEvent>(new BoundedChannelOptions(capacity)
        {
            SingleReader = true,
            SingleWriter = false,
            FullMode = fullMode
        });
        queue = channel;

        _consumerTask = Task.Run(() => ConsumeLoop(channel.Reader, _cts.Token));
    }

    // 订阅
    public IDisposable Subscribe<TEvent>(string topic, Func<TEvent, Task> handler, SubscribeOptions? options = null)
    {
        if (string.IsNullOrWhiteSpace(topic))
            throw new ArgumentException("topic is required", nameof(topic));
        if (handler is null)
            throw new ArgumentNullException(nameof(handler));
        ThrowIfDisposed();

        options ??= new SubscribeOptions();

        var list = subscribers.GetOrAdd(topic, _ => new List<IHandler>());
        var h = new Handler<TEvent>(handler, options);

        lock (list)
        {
            list.Add(h);
            // 按优先级降序排列,保证分发顺序
            list.Sort(static (a, b) => b.Priority.CompareTo(a.Priority));
        }

        // 新订阅者立即获取粘性事件
        if (enableReplay && options.ReplayStickyImmediately)
        {
            IEvent? sticky = null;
            lock (_stickyLock)
            {
                if (stickyEvents.TryGetValue(topic, out var evt))
                {
                    sticky = evt;
                }
            }

            if (sticky is not null)
            {
                // 单独投递给该 handler(不影响其他订阅者)
                _ = DispatchToSingleHandlerAsync(topic, sticky, h, _cts.Token);
            }
        }

        // 返回可释放对象用于取消订阅
        return new Unsubscriber(() => Unsubscribe(topic, h));
    }

    // 取消订阅(通过原始委托匹配)
    public void Unsubscribe<TEvent>(string topic, Func<TEvent, Task> handler)
    {
        if (string.IsNullOrWhiteSpace(topic))
            return;

        if (!subscribers.TryGetValue(topic, out var list))
            return;

        lock (list)
        {
            var toRemove = list
                .OfType<Handler<TEvent>>()
                .FirstOrDefault(h => ReferenceEquals(h.OriginalDelegate, handler));
            if (toRemove != null)
            {
                list.Remove(toRemove);
                toRemove.Dispose();
            }
        }
    }

    // 内部取消订阅重载(用于一次性订阅完成后自动移除)
    private void Unsubscribe(string topic, IHandler handler)
    {
        if (!subscribers.TryGetValue(topic, out var list))
            return;

        lock (list)
        {
            if (list.Remove(handler))
            {
                (handler as IDisposable)?.Dispose();
            }
        }
    }

    // 发布
    public async Task Publish<TEvent>(string topic, TEvent evt, PublishOptions? options = null, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(topic))
            throw new ArgumentException("topic is required", nameof(topic));
        ThrowIfDisposed();

        options ??= new PublishOptions();

        if (options.Delay is { } delay && delay > TimeSpan.Zero)
        {
            await Task.Delay(delay, cancellationToken).ConfigureAwait(false);
        }

        var envelope = CreateEnvelope(topic, evt!, options);

        // 事务性:若携带事务,则缓冲,等待 Commit
        if (options.Transaction is not null)
        {
            options.Transaction.Enqueue(envelope);
            return;
        }

        // 粘性事件存储(立即保存最后一次)
        if (enableReplay && options.Sticky)
        {
            lock (_stickyLock)
            {
                stickyEvents[topic] = envelope;
            }
        }

        if (options.Synchronous)
        {
            // 同步:直接执行中间件和分发
            IncrementEnqueued();
            try
            {
                var processed = await RunMiddlewares(envelope, cancellationToken).ConfigureAwait(false);
                if (processed != null)
                {
                    await DispatchToSubscribers(topic, processed, cancellationToken).ConfigureAwait(false);
                }
            }
            finally
            {
                IncrementProcessed();
            }
        }
        else
        {
            // 异步:入队,背压由 Channel 控制
            IncrementEnqueued();
            try
            {
                await queue.Writer.WriteAsync(envelope, cancellationToken).ConfigureAwait(false);
            }
            catch
            {
                // 写入失败则回退计数
                IncrementProcessed();
                throw;
            }
        }
    }

    // 注册中间件
    public void Use(Func<IEvent, Task<IEvent?>> middleware)
    {
        if (middleware is null) throw new ArgumentNullException(nameof(middleware));
        ThrowIfDisposed();
        lock (middlewares)
        {
            middlewares.Add(middleware);
        }
    }

    // 重放主题的粘性事件
    public async Task Replay(string topic, CancellationToken cancellationToken = default)
    {
        if (!enableReplay) return;
        if (string.IsNullOrWhiteSpace(topic)) return;

        IEvent? sticky;
        lock (_stickyLock)
        {
            stickyEvents.TryGetValue(topic, out sticky);
        }
        if (sticky is null) return;

        // 重放按当前模式走(异步分发),不改变原始粘性标志
        IncrementEnqueued();
        try
        {
            await queue.Writer.WriteAsync(sticky, cancellationToken).ConfigureAwait(false);
        }
        catch
        {
            IncrementProcessed();
            throw;
        }
    }

    // 等待队列清空与在途事件处理完毕(用于测试或优雅关闭)
    public Task Flush(CancellationToken cancellationToken = default)
    {
        // 若当前空闲,直接返回
        var tcs = _idleTcs;
        if (IsIdle())
            return Task.CompletedTask;

        // 等待空闲或取消
        return WaitOrCancelAsync(tcs.Task, cancellationToken);
    }

    public void Dispose()
    {
        if (_disposed) return;
        _disposed = true;

        try
        {
            queue.Writer.TryComplete();
        }
        catch { /* ignore */ }

        try
        {
            _cts.Cancel();
        }
        catch { /* ignore */ }

        try
        {
            _consumerTask.Wait(TimeSpan.FromSeconds(3));
        }
        catch { /* ignore */ }

        _cts.Dispose();

        // 释放所有 handler 资源
        foreach (var kv in subscribers)
        {
            lock (kv.Value)
            {
                foreach (var h in kv.Value.OfType<IDisposable>())
                {
                    h.Dispose();
                }
                kv.Value.Clear();
            }
        }
    }

    // 事务对象(可选使用:将 PublishOptions.Transaction 指定为该对象)
    public sealed class EventBusTransaction : IDisposable
    {
        private readonly EventBus _bus;
        private readonly List<IEvent> _buffer = new();
        private bool _committed;
        private bool _disposed;

        internal EventBusTransaction(EventBus bus)
        {
            _bus = bus;
        }

        public void Enqueue(IEvent evt)
        {
            if (_disposed) throw new ObjectDisposedException(nameof(EventBusTransaction));
            _buffer.Add(evt);
            // 粘性事件在事务阶段也要覆盖保存
            if (_bus.enableReplay && evt.Sticky)
            {
                lock (_bus._stickyLock)
                {
                    _bus.stickyEvents[evt.Topic] = evt;
                }
            }
        }

        public async Task CommitAsync(CancellationToken cancellationToken = default)
        {
            if (_disposed) throw new ObjectDisposedException(nameof(EventBusTransaction));
            if (_committed) return;

            foreach (var evt in _buffer)
            {
                _bus.IncrementEnqueued();
                try
                {
                    await _bus.queue.Writer.WriteAsync(evt, cancellationToken).ConfigureAwait(false);
                }
                catch
                {
                    _bus.IncrementProcessed();
                    throw;
                }
            }

            _buffer.Clear();
            _committed = true;
        }

        public void Dispose()
        {
            if (_disposed) return;
            _disposed = true;
            _buffer.Clear();
        }
    }

    // 若需要:外部可自行为一组 Publish 创建事务并传入 PublishOptions.Transaction
    public EventBusTransaction CreateTransaction() => new EventBusTransaction(this);

    // ========== 内部实现 ==========

    private static TaskCompletionSource<bool> CreateIdleTcs(bool completed)
    {
        var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
        if (completed) tcs.TrySetResult(true);
        return tcs;
    }

    private void IncrementEnqueued()
    {
        var current = Interlocked.Increment(ref _enqueued);
        if (current - Volatile.Read(ref _processed) == 1)
        {
            // 从空闲变为非空闲,重置 TCS
            _idleTcs = CreateIdleTcs(completed: false);
        }
    }

    private void IncrementProcessed()
    {
        var processed = Interlocked.Increment(ref _processed);
        if (processed == Volatile.Read(ref _enqueued))
        {
            _idleTcs.TrySetResult(true);
        }
    }

    private bool IsIdle() => Volatile.Read(ref _enqueued) == Volatile.Read(ref _processed);

    private static async Task WaitOrCancelAsync(Task task, CancellationToken ct)
    {
        using var reg = ct.CanBeCanceled
            ? ct.Register(state => ((TaskCompletionSource<bool>)state!).TrySetCanceled(), new TaskCompletionSource<bool>(), useSynchronizationContext: false)
            : default;

        var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
        using (ct.Register(() => tcs.TrySetCanceled(), useSynchronizationContext: false))
        {
            var completed = await Task.WhenAny(task, tcs.Task).ConfigureAwait(false);
            await completed.ConfigureAwait(false);
        }
    }

    private void ThrowIfDisposed()
    {
        if (_disposed) throw new ObjectDisposedException(nameof(EventBus));
    }

    private IEvent CreateEnvelope(string topic, object payload, PublishOptions options)
    {
        var metadata = options.Metadata is null
            ? new Dictionary<string, object?>()
            : new Dictionary<string, object?>(options.Metadata);

        return new EventEnvelope(
            topic: topic,
            payload: payload,
            payloadType: payload.GetType(),
            timestamp: DateTimeOffset.UtcNow,
            sticky: options.Sticky,
            metadata: metadata
        );
    }

    private async Task ConsumeLoop(ChannelReader<IEvent> reader, CancellationToken ct)
    {
        try
        {
            await foreach (var evt in reader.ReadAllAsync(ct).ConfigureAwait(false))
            {
                try
                {
                    var processed = await RunMiddlewares(evt, ct).ConfigureAwait(false);
                    if (processed != null)
                    {
                        await DispatchToSubscribers(processed.Topic, processed, ct).ConfigureAwait(false);
                    }
                }
                catch (OperationCanceledException) when (ct.IsCancellationRequested)
                {
                    // shutting down
                }
                catch (Exception ex)
                {
                    logger?.LogError(ex, "EventBus: error while processing event for topic {Topic}", evt.Topic);
                }
                finally
                {
                    IncrementProcessed();
                }
            }
        }
        catch (OperationCanceledException)
        {
            // ignore
        }
        catch (Exception ex)
        {
            logger?.LogError(ex, "EventBus: consumer loop crashed.");
        }
    }

    private async Task<IEvent?> RunMiddlewares(IEvent evt, CancellationToken ct)
    {
        IEvent? current = evt;
        List<Func<IEvent, Task<IEvent?>>> snapshot;
        lock (middlewares)
        {
            snapshot = middlewares.ToList();
        }

        foreach (var mw in snapshot)
        {
            ct.ThrowIfCancellationRequested();
            try
            {
                if (current is null) return null;
                current = await mw(current).ConfigureAwait(false);
                if (current is null) return null; // 拦截
            }
            catch (Exception ex)
            {
                logger?.LogError(ex, "EventBus: middleware failed for topic {Topic}", evt.Topic);
                // 中间件抛错默认终止该事件分发
                return null;
            }
        }

        return current;
    }

    private async Task DispatchToSubscribers(string topic, IEvent evt, CancellationToken ct)
    {
        if (!subscribers.TryGetValue(topic, out var list) || list.Count == 0)
            return;

        IHandler[] handlers;
        lock (list)
        {
            // 拍快照,避免分发时与修改冲突
            handlers = list.ToArray();
        }

        // 并行分发到不同 handler;每个 handler 自身用 Semaphore 控制并发
        var tasks = new List<Task>(handlers.Length);
        foreach (var h in handlers)
        {
            if (!h.EventType.IsAssignableFrom(evt.PayloadType))
                continue;

            tasks.Add(DispatchToSingleHandlerAsync(topic, evt, h, ct));
        }

        if (tasks.Count > 0)
            await Task.WhenAll(tasks).ConfigureAwait(false);
    }

    private async Task DispatchToSingleHandlerAsync(string topic, IEvent evt, IHandler handler, CancellationToken ct)
    {
        await handler.Semaphore.WaitAsync(ct).ConfigureAwait(false);
        try
        {
            await handler.Invoke(evt, ct).ConfigureAwait(false);

            if (handler.Once)
            {
                // 成功后一次性订阅自动移除
                Unsubscribe(topic, handler);
            }
        }
        catch (OperationCanceledException) when (ct.IsCancellationRequested)
        {
            // shutting down
        }
        catch (Exception ex)
        {
            logger?.LogError(ex, "EventBus: handler execution failed on topic {Topic}", topic);
        }
        finally
        {
            handler.Semaphore.Release();
        }
    }

    // Envelope 实现
    private sealed class EventEnvelope : IEvent
    {
        public string Topic { get; }
        public object Payload { get; }
        public Type PayloadType { get; }
        public DateTimeOffset Timestamp { get; }
        public bool Sticky { get; }
        public IReadOnlyDictionary<string, object?> Metadata { get; }

        public EventEnvelope(string topic, object payload, Type payloadType, DateTimeOffset timestamp, bool sticky, Dictionary<string, object?> metadata)
        {
            Topic = topic;
            Payload = payload;
            PayloadType = payloadType;
            Timestamp = timestamp;
            Sticky = sticky;
            Metadata = metadata;
        }
    }

    // Handler 实现(泛型包装 Func<TEvent, Task>)
    private sealed class Handler<TEvent> : IHandler, IDisposable
    {
        private readonly Func<TEvent, Task> _handler;

        public int Priority { get; }
        public int MaxConcurrency { get; }
        public bool Once { get; }
        public Type EventType => typeof(TEvent);
        public Delegate OriginalDelegate => _handler;
        public SemaphoreSlim Semaphore { get; }

        public Handler(Func<TEvent, Task> handler, SubscribeOptions options)
        {
            _handler = handler ?? throw new ArgumentNullException(nameof(handler));
            Priority = options.Priority;
            MaxConcurrency = Math.Max(1, options.MaxConcurrency);
            Once = options.Once;
            Semaphore = new SemaphoreSlim(MaxConcurrency, MaxConcurrency);
        }

        public Task Invoke(IEvent evt, CancellationToken ct)
        {
            // 中间件保证类型匹配,这里做一次防御
            if (evt.Payload is not TEvent typed)
                throw new InvalidOperationException($"Payload type mismatch. Expected {typeof(TEvent).Name}, actual {evt.PayloadType.Name}.");
            return _handler(typed);
        }

        public void Dispose()
        {
            Semaphore.Dispose();
        }
    }

    private sealed class Unsubscriber : IDisposable
    {
        private readonly Action _action;
        private int _disposed;

        public Unsubscriber(Action action) => _action = action;

        public void Dispose()
        {
            if (Interlocked.Exchange(ref _disposed, 1) == 0)
            {
                _action();
            }
        }
    }
}

}

使用示例(简要):

  • 创建与订阅 var bus = new LightweightEventBus.EventBus(enableReplay: true, logger: logger); var d = bus.Subscribe("user.created", async s => Console.WriteLine($"User: {s}"), new SubscribeOptions { Priority = 10, MaxConcurrency = 2, Once = false });

  • 发布(异步) await bus.Publish("user.created", "Alice", new PublishOptions { Sticky = true });

  • 发布(同步,并延迟 100ms) await bus.Publish("user.created", "Bob", new PublishOptions { Synchronous = true, Delay = TimeSpan.FromMilliseconds(100) });

  • 事务性发布 var tx = bus.CreateTransaction(); await bus.Publish("order.paid", new { Id = 1 }, new PublishOptions { Transaction = tx, Sticky = true }); await tx.CommitAsync();

  • 重放与 Flush await bus.Replay("user.created"); await bus.Flush();

  • 释放 d.Dispose(); // 取消订阅 bus.Dispose();

设计说明:

  • 中间件链按注册顺序执行,返回 null 表示拦截该事件。
  • 订阅列表使用 List + lock 确保插入/删除/排序安全;读取时快照数组避免锁持有时间过长。
  • 背压:Channel 为有界队列(默认 1024,满时等待),可通过构造参数调整容量与 FullMode。
  • 粘性存储:按 topic 存最后一次事件;新订阅可立即获粘性事件(可通过 SubscribeOptions 控制)。
  • Flush 通过入队/处理计数对实现,保证异步队列与在途事件处理完成。

示例详情

该提示词已被收录:
“程序员必备:提升开发效率的专业AI提示词合集”
让 AI 成为你的第二双手,从代码生成到测试文档全部搞定,节省 80% 开发时间
√ 立即可用 · 零学习成本
√ 参数化批量生成
√ 专业提示词工程师打磨

📖 如何使用

30秒出活:复制 → 粘贴 → 搞定
与其花几十分钟和AI聊天、试错,不如直接复制这些经过千人验证的模板,修改几个 {{变量}} 就能立刻获得专业级输出。省下来的时间,足够你轻松享受两杯咖啡!
加载中...
💬 不会填参数?让 AI 反过来问你
不确定变量该填什么?一键转为对话模式,AI 会像资深顾问一样逐步引导你,问几个问题就能自动生成完美匹配你需求的定制结果。零门槛,开口就行。
转为对话模式
🚀 告别复制粘贴,Chat 里直接调用
无需切换,输入 / 唤醒 8000+ 专家级提示词。 插件将全站提示词库深度集成于 Chat 输入框。基于当前对话语境,系统智能推荐最契合的 Prompt 并自动完成参数化,让海量资源触手可及,从此彻底告别"手动搬运"。
即将推出
🔌 接口一调,提示词自己会进化
手动跑一次还行,跑一百次呢?通过 API 接口动态注入变量,接入批量评价引擎,让程序自动迭代出更高质量的提示词方案。Prompt 会自己进化,你只管收结果。
发布 API
🤖 一键变成你的专属 Agent 应用
不想每次都配参数?把这条提示词直接发布成独立 Agent,内嵌图片生成、参数优化等工具,分享链接就能用。给团队或客户一个"开箱即用"的完整方案。
创建 Agent

✅ 特性总结

快速生成面向对象的类结构,囊括属性和方法,优化开发效率。
智能支持多种编程语言的类设计,满足多样化开发需求。
助力实现复杂设计模式,轻松完成工厂、单例等常用模式的集成。
自动化生成结构化代码模板,确保一致性与高质量代码输出。
轻松定制类名、属性和方法,避免重复设计,提升开发体验。
快速添加构造函数和特定属性初始化逻辑,加速项目开发流程。
支持领域特定的个性化类设计,从业务场景需求出发精确建模。
优化代码结构,有助于遵循最佳开发实践,减少手工错误。
创建清晰、可维护的类层次,简化复杂系统的架构设计。

🎯 解决的问题

帮助开发者快速生成符合面向对象编程规范的类结构,包括属性、方法及可选的设计模式,从而提高开发效率,简化重复性工作,助力软件设计优化。

🕒 版本历史

当前版本
v2.1 2024-01-15
优化输出结构,增强情节连贯性
  • ✨ 新增章节节奏控制参数
  • 🔧 优化人物关系描述逻辑
  • 📝 改进主题深化引导语
  • 🎯 增强情节转折点设计
v2.0 2023-12-20
重构提示词架构,提升生成质量
  • 🚀 全新的提示词结构设计
  • 📊 增加输出格式化选项
  • 💡 优化角色塑造引导
v1.5 2023-11-10
修复已知问题,提升稳定性
  • 🐛 修复长文本处理bug
  • ⚡ 提升响应速度
v1.0 2023-10-01
首次发布
  • 🎉 初始版本上线
COMING SOON
版本历史追踪,即将启航
记录每一次提示词的进化与升级,敬请期待。

💬 用户评价

4.8
⭐⭐⭐⭐⭐
基于 28 条评价
5星
85%
4星
12%
3星
3%
👤
电商运营 - 张先生
⭐⭐⭐⭐⭐ 2025-01-15
双十一用这个提示词生成了20多张海报,效果非常好!点击率提升了35%,节省了大量设计时间。参数调整很灵活,能快速适配不同节日。
效果好 节省时间
👤
品牌设计师 - 李女士
⭐⭐⭐⭐⭐ 2025-01-10
作为设计师,这个提示词帮我快速生成创意方向,大大提升了工作效率。生成的海报氛围感很强,稍作调整就能直接使用。
创意好 专业
COMING SOON
用户评价与反馈系统,即将上线
倾听真实反馈,在这里留下您的使用心得,敬请期待。
加载中...