V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
auv1107
V2EX  ›  程序员

分享下我怎么用 Cloudflare Worker 一个文件为我的出海产品支持购买力平价能力的

  •  1
     
  •   auv1107 · 323 天前 · 2404 次点击
    这是一个创建于 323 天前的主题,其中的信息可能已经有所发展或是发生改变。

    由于各个国家、地区购买力不一,出海产品需要为不同购买力的地区设定不同的价格。

    实现购买力平价需要两个数据:

    1. 用户的位置数据
    2. 一份各国家的购买力水平清单

    逻辑就很简单了: 位置数据 -> 购买力水平 -> 匹配相应价格信息并应用

    那么这两个数据怎么获取呢?

    位置数据

    Cloudflare Worker 的 request 参数带了非常详细的位置数据,这让我们不需要再使用第三方服务就能方便地获取位置:

    它包括了经纬度、地区代码等,我们的颗粒度精确到国家,这里用国家代码 country 字段

    购买力水平数据

    购买力水平等级可以在这个 gist 下载:各国家购买力水平清单

    逻辑和实现代码

    大致逻辑是:

    1. 从 Worker 的 request 参数获取国家代码
    2. 使用国家代码从购买能力列表获取购买力水平
    3. 根据水平匹配相应的折扣信息并应用

    我在 CleanClip(Mac 上的剪贴板工具) 中简单起见,直接为不同国家应用不同的折扣。 LemonSqueezy 可以这样直接应用折扣码:PRODUCT_URL + "?checkout%5Bdiscount_code%5D=" + discountCode

    一些细节:

    • 折扣信息保存在环境变量里,方便随时修改
    • Access-Control-Max-Age 缓存设为 0 ,可方便随时改动,即时生效。(不设置会导致上次结果保留过久,实践大概是 3 、4 天左右才生效,设为 0 即时生效)
    • 可以将这个 worker 连接到其它 worker 下,价格信息在这里统一维护,方便多页面、业务使用
    import ppp from "./pppdata.js";
    
    // map 一下购买力数据的列表,方便搜索
    const flatppp = ppp.flatMap(category => category.countries.map( countryInfo => {
      return {
        range: category.range,
        countryCode: countryInfo.country,
        countryName: countryInfo.countryName
      }
    }))
    
    // 在购买力水平列表中找到对应国家
    function findCountry(countryCode) {
      return flatppp.find(deal => deal.countryCode == countryCode)
    }
    
    // 根据购买力水平,在环境变量里找到配置的折扣信息
    function getDiscount(env, range) {
      switch(range) {
        case "0.0-0.1": return { code: env.level0_1 ?? "", discount: parseInt(env.level0_1_discount ?? "0") ?? 0 }
        case "0.1-0.2": return { code: env.level1_2 ?? "", discount: parseInt(env.level1_2_discount ?? "0") ?? 0 }
        case "0.2-0.3": return { code: env.level2_3 ?? "", discount: parseInt(env.level2_3_discount ?? "0") ?? 0 }
        case "0.3-0.4": return { code: env.level3_4 ?? "", discount: parseInt(env.level3_4_discount ?? "0") ?? 0 }
        case "0.4-0.5": return { code: env.level4_5 ?? "", discount: parseInt(env.level4_5_discount ?? "0") ?? 0 }
        case "0.5-0.6": return { code: env.level5_6 ?? "", discount: parseInt(env.level5_6_discount ?? "0") ?? 0 }
        case "0.6-0.7": return { code: env.level6_7 ?? "", discount: parseInt(env.level6_7_discount ?? "0") ?? 0 }
        case "0.7-0.8": return { code: env.level7_8 ?? "", discount: parseInt(env.level7_8_discount ?? "0") ?? 0 }
        case "0.8-0.9": return { code: env.level8_9 ?? "", discount: parseInt(env.level8_9_discount ?? "0") ?? 0 }
        case "0.9-1.0": return { code: env.level9_10 ?? "", discount: parseInt(env.level9_10_discount ?? "0") ?? 0 }
        case "1.0-1.1": return { code: env.level10_11 ?? "", discount: parseInt(env.level10_11_discount ?? "0") ?? 0 }
        case "1.1-1.2": return { code: env.level11_12 ?? "", discount: parseInt(env.level11_12_discount ?? "0") ?? 0 }
        case "1.2-1.3": return { code: env.level12_13 ?? "", discount: parseInt(env.level12_13_discount ?? "0") ?? 0 }
        case "1.3-1.4": return { code: env.level13_14 ?? "", discount: parseInt(env.level13_14_discount ?? "0") ?? 0 }
        default: return {code: "", discount: 0}
      }
    }
    
    // 合并国家购买力信息+折扣信息
    function mergeDiscountResult(countryPPP, discount) {
      return JSON.stringify({
        range: countryPPP.range,
        countryCode: countryPPP.countryCode,
        countryName: countryPPP.countryName,
        discountCode: discount.code,
        discount: discount.discount
      });
    }
    
    // 构造 response
    function responseFor(result, code) {
      return new Response(result, {
        status: code,
        headers: {
          "Content-Type": "application/json",
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Headers": "*",
          "Access-Control-Allow-Methods": "GET, OPTIONS, POST, PUT, DELETE",
          "Access-Control-Max-Age": "0"
        }
      });
    }
    
    // ✨ 核心代码
    export default {
      async fetch(request, env, ctx) {
        // 1. 获取国家编码
        const countryCode = request.cf.country
    
        // 2. 在购买力列表中找到该国家
        let countryPPP = findCountry(countryCode)
    
        // 3. 通过该国家购买力获取对应优惠信息
        let discount = getDiscount(env, countryPPP.range)
    
        if (countryPPP && discount) {
          // 构造结果
          let result = mergeDiscountResult(countryPPP, discount)
          // 4. 可以直接返回结果供其它服务调用
          return responseFor(result, 200)
        } else {
          return responseFor("Error", 500)
        }
    
        // 5. 或者直接 301 重定向到指定优惠链接
        // let url = env.TARGET_DOMAIN
        // if (discountCode !== undefined && discountCode.length > 0) {
        //   url = env.TARGET_DOMAIN + "?checkout%5Bdiscount_code%5D=" + discountCode
        // }
        // var response = Response.redirect(url, 301);
      },
    };
    

    原文地址: https://cleanclip.cc/zh/developer/cloudflare-worker-implements-purchasing-power-parity/

    10 条回复    2024-02-13 18:34:39 +08:00
    Xinu
        1
    Xinu  
       323 天前   ❤️ 1
    学到了 👍
    ByteCat
        2
    ByteCat  
       323 天前   ❤️ 1
    确实能获取请求的各种地理信息,我之前还拿来当查 ip 用,https://api.imbytecat.com/ipinfo
    fred649
        3
    fred649  
       323 天前   ❤️ 1
    XJ 也能直連 Cloudflare 真是可喜可賀
    auv1107
        4
    auv1107  
    OP
       323 天前
    @ByteCat 是的,好用!
    auv1107
        5
    auv1107  
    OP
       323 天前
    @fred649 你能看到我的 ip ??
    kkk9
        6
    kkk9  
       323 天前
    @auv1107 #5 你自己贴图里那么大的 XJ xinjiang …… 😅😅😅
    auv1107
        7
    auv1107  
    OP
       323 天前
    @kkk9 啊哈哈 笑死了 忘掉了
    yulgang
        8
    yulgang  
       323 天前 via iPhone
    @auv1107 哈哈哈哈哈哈😂
    kaiveyoung
        9
    kaiveyoung  
       321 天前 via Android
    那么大的Ürümq
    hanguofu
        10
    hanguofu  
       318 天前
    如果用户是通过 代理 来访问 咋办 ?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2788 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 02:56 · PVG 10:56 · LAX 18:56 · JFK 21:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.