使用golang灵活处理动态文案

代码

package scripts_stroage

import (
    "fmt"
    "github.com/duke-git/lancet/v2/slice"
    "github.com/gogf/gf/util/gconv"
    "github.com/gookit/goutil/dump"
    "regexp"
    "strings"
    "testing"
)

const (

    // 有些变量variableMap中没有,但是传来的fmt字符串中有,需要加固定的逻辑校验
    userNameVariable = "${userName}"

    // 有些变量优先使用传来的,如果传来的是空 需要从其他地方获取
    taskNameVariable = "${taskName}"

    // 模拟的一个用户名 TODO 实际上会从 数据库/Redis/ctx 中获取~
    userNameModel = "Naruto火影大人"
)

func copyWritingConfig() (variableMap map[string]interface{}, copyWritingFmtText string) {

    variableMap = make(map[string]interface{})

    // 文案的 变量名、变量值 —— Notice 数量不固定
    variableMap["${taskName}"] = "实况足球以10:0完成比赛任务"
    variableMap["${rewardUrl}"] = `<a href="/reward/get">奖励页面</a>`

    variableMap["${rechargeUrl}"] = `<a href="/recharge/get">充值页面</a>`
    variableMap["${tenYuan}"] = "10元"
    variableMap["${tenYuanReward}"] = "抽取哈兰德机会卡。"
    // TODO ...

    // 文案格式化的字符串 ———— 需要提前约定好变量名的含义
    copyWritingFmtText = "尊敬的用户:${userName},恭喜您完成了${taskName},请立刻前往${rewardUrl}去领取。\n" +
        "另外如果您在${rechargeUrl}充值${tenYuan},您将获得${tenYuanReward}"

    return
}

type CopyWritingMap map[string]interface{}

func (c CopyWritingMap) keys() []any {
    keys := make([]any, 0)
    for key := range c {
        keys = append(keys, key)
    }

    return keys
}

// 使用正则匹配出 ${xxx} 这样的字符串
func GetCopyWritingVariableName(fmtStr string) []string {

    // 预编译
    re := regexp.MustCompile(`\$\{\w+}`)
    matches := re.FindAllString(fmtStr, -1)

    return matches
}

// TODO 实际中的需求是:在管理后台配置好文案变量以及变量对应的值、文案格式化的字符串,C端的接口拿着这些配置去生成文案
// TODO 如果直接把配置一点都不处理的存下来,下面的代码放在C端接口执行的话那么效率还是非常低的(有正则匹配、大量的字符串替换)
// TODO 实际上,我们可以在"管理后台存储"的时候,将文案格式化的字符串"预处理一下":比如把正则匹配的逻辑放到管理后台接口,先生成一个"经过预处理的格式化文案"
// TODO 对于那些需要C端接口获取的变量 比如 ${userName},当我们遇到这样的变量后,再查一遍然后替换"经过预处理的格式化文案"中对应的变量即可
// TODO 管理后台预处理后可以暂时将文案存起来:
/*
    // 关键字段
    foreign_key_xxx(比如绑定任务id、用户id等等):xxx
    pre_copy_writing_fmt_text: "尊敬的用户:${userName},恭喜您完成了实况足球赛季2任务,请立刻前往<a href="/reward/get">奖励页面</a>去领取。"
    variable_list(json序列化的text类型的字段,为空记录[],数量必须跟上面的变量的数量一样): ["${userName}"]
*/

func TestGenCopyWriting(t *testing.T) {

    variableMap, copyWritingFmtText := copyWritingConfig()
    if len(variableMap) < 1 || copyWritingFmtText == "" {
        return
    }

    // 使用正则匹配 ${xxx} 有哪些.....
    allVariables := GetCopyWritingVariableName(copyWritingFmtText)
    // Notice 没有变量 直接返回
    if len(allVariables) == 0 {
        fmt.Println("没有变量! ")
        return
    }

    fmt.Println("allVariables: ", allVariables)

    cpWritingMap := CopyWritingMap{}

    // Notice 1、variableMap中 需要单独查询的变量
    if slice.Contain(allVariables, userNameVariable) {
        // TODO 实际上应该从其他地方查用户名,这里省略了...
        cpWritingMap[userNameVariable] = userNameModel
    }

    for key := range variableMap {
        // Notice 对于 taskName,优先使用传来的,如果传来的是空需要通过RPC获取任务名
        if key == taskNameVariable && variableMap[key] == "" {
            // TODO 通过RPC获取任务名再构建variableMap,
            // TODO 加完后记得continue...略...
        }

        if gconv.String(variableMap[key]) != "" {
            cpWritingMap[key] = variableMap[key]
        }
    }

    fmt.Println(">>>>>>cpWritingMap: ")
    dump.P(cpWritingMap)

    if len(cpWritingMap) < 1 {
        return
    }
    // Notice 实际上 cpWritingMap 与 allVariables 的长度应该一样!
    if len(cpWritingMap) != len(allVariables) {
        fmt.Printf("allVariables的长度与cpWritingMap的长度不一致!")
        dump.P(allVariables)
        dump.P(cpWritingMap)
        return
    }

    // 使用 strings.Replace 进行替换
    for key := range cpWritingMap {
        copyWritingFmtText = strings.Replace(copyWritingFmtText, key, gconv.String(cpWritingMap[key]), -1)
    }

    fmt.Println("result: ", copyWritingFmtText)
    /*
        尊敬的用户:Naruto火影大人,恭喜您完成了实况足球以10:0完成比赛任务,请立刻前往<a href="/reward/get">奖励页面</a>去领取。
        另外如果您在<a href="/recharge/get">充值页面</a>充值10元,您将获得抽取哈兰德机会卡。
    */
}

~~~

posted on 2023-07-22 20:49  江湖乄夜雨  阅读(35)  评论(0编辑  收藏  举报