使用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元,您将获得抽取哈兰德机会卡。 */ }
~~~