products.js 1.7 KB
/**
 * `products.js` —— 产品目录读取(积分套餐 + 可购权益)。
 *
 * 产品目录,数据全来自 `billingRepo`(运营可改)。`save` 保存完整产品定义;调用方不传的
 * 可选字段会按默认值或 null 入库,不做隐式局部 patch。
 *
 * @typedef {import('./repo/index.js').BillingRepo} BillingRepo
 */

import { BillingError, BillingErrorCode } from './errors.js'

/**
 * @param {BillingRepo} billingRepo
 */
export function createProducts(billingRepo) {
  return {
    /** @returns {Promise<Product[]>} */
    async list() {
      return billingRepo.listProducts()
    },
    async listAll() {
      return billingRepo.listAllProducts()
    },
    /**
     * @param {string} productId
     * @returns {Promise<Product|null>}
     */
    async get(productId) {
      const p = await billingRepo.findProduct(productId)
      return p ?? null
    },
    /**
     * @param {Record<string, any>} product
     */
    async save(product) {
      if (!product?.id) {
        throw new BillingError({
          code: BillingErrorCode.PRODUCT_NOT_FOUND,
          message: '产品 id 不能为空',
          retryable: false,
        })
      }
      return billingRepo.saveProduct(product)
    },
    /**
     * 取产品,缺失即抛(供需要强存在性的编排用,如 payment 下单)。
     * @param {string} productId
     * @returns {Promise<Product>}
     */
    async require(productId) {
      const p = await billingRepo.findProduct(productId)
      if (!p) {
        throw new BillingError({
          code: BillingErrorCode.PRODUCT_NOT_FOUND,
          message: `产品不存在: ${productId}`,
          retryable: false,
        })
      }
      return p
    },
  }
}