ai_scheduler/internal/biz/do/macro.go

289 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package do
import (
"ai_scheduler/internal/data/impl"
"ai_scheduler/internal/data/model"
"ai_scheduler/internal/pkg"
"ai_scheduler/internal/tools/bbxt"
"context"
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"unicode"
"github.com/gofiber/fiber/v2/log"
"xorm.io/builder"
)
type Macro struct {
botGroupImpl *impl.BotGroupImpl
reportDailyCacheImpl *impl.ReportDailyCacheImpl
}
func NewMacro(
botGroupImpl *impl.BotGroupImpl,
reportDailyCacheImpl *impl.ReportDailyCacheImpl,
) *Macro {
return &Macro{
botGroupImpl: botGroupImpl,
reportDailyCacheImpl: reportDailyCacheImpl,
}
}
type MacroFunc func(ctx context.Context, content string, groupConfig *model.AiBotGroupConfig) (successMsg string, err error, isFinish bool)
func (m *Macro) Router(ctx context.Context, reqContent string, groupConfig *model.AiBotGroupConfig) (successMsg string, err error, isFinish bool) {
reqContent = strings.TrimSpace(reqContent)
switch {
case strings.HasPrefix(reqContent, "[利润同比报表]商品修改"):
return m.ProductModify(ctx, reqContent, groupConfig)
case strings.HasPrefix(reqContent, "[利润同比报表]商品列表"):
return m.ProductList(ctx, groupConfig)
case strings.HasPrefix(reqContent, "[负利润分析]导出"):
return m.NegativeProfitGet(ctx)
case strings.HasPrefix(reqContent, "[负利润分析]更新"):
return m.NegativeProfitUpdate(ctx, reqContent, groupConfig)
default:
}
return
}
func (m *Macro) NegativeProfitUpdate(ctx context.Context, content string, groupConfig *model.AiBotGroupConfig) (successMsg string, err error, isFinish bool) {
//newContent := strings.ReplaceAll(strings.TrimSpace(content), "[负利润分析]更新:", "")
jsonData, err := ParseLossData(content)
if err != nil {
err = fmt.Errorf("解析失败:%v", err)
return
}
dayDate := time.Now().Format(time.DateOnly)
cond := builder.NewCond()
cond = cond.And(builder.Eq{"cache_index": bbxt.IndexLossSumDetail})
cond = cond.And(builder.Eq{"cache_key": dayDate})
err = m.reportDailyCacheImpl.UpdateByCond(&cond, &model.AiReportDailyCache{
Value: pkg.JsonStringIgonErr(jsonData),
})
if err != nil {
err = fmt.Errorf("解析失败:%v", err)
return
}
isFinish = true
successMsg = "更新成功"
return
}
func (m *Macro) NegativeProfitGet(ctx context.Context) (successMsg string, err error, isFinish bool) {
var (
data model.AiReportDailyCache
value map[int32]*bbxt.ResellerLossSumProductRelation
)
isFinish = true
cond := builder.NewCond()
cond = cond.And(builder.Eq{"cache_index": bbxt.IndexLossSumDetail})
cond = cond.And(builder.Eq{"cache_key": time.Now().Format(time.DateOnly)})
err = m.reportDailyCacheImpl.GetOneBySearchToStrut(&cond, &data)
if err != nil {
err = fmt.Errorf("获取失败:%v", err)
return
}
if data.ID == 0 {
successMsg = "暂未获取今日负利润分析数据,请先呼出报表数据"
return
}
err = json.Unmarshal([]byte(data.Value), &value)
if err != nil {
err = fmt.Errorf("获取失败,格式解析错误:%v", err)
return
}
//将value转为string
//**[供应商id]供应商名称->商务名称**\n
//└──商品名称:亏损原因\n
var valueString strings.Builder
valueString.WriteString("[负利润分析]更新:\n")
for k, v := range value {
if len(v.AfterSaleName) == 0 {
v.AfterSaleName = "未查找到对应商务"
}
valueString.WriteString(fmt.Sprintf("**[%d]%s->%s**\n", k, v.ResellerName, v.AfterSaleName))
for kk, vv := range v.Products {
valueString.WriteString(fmt.Sprintf("└── [%d]%s:%s\n", kk, vv.ProductName, vv.LossReason))
}
}
successMsg = valueString.String()
return
}
func (m *Macro) ProductModify(ctx context.Context, content string, groupConfig *model.AiBotGroupConfig) (successMsg string, err error, isFinish bool) {
content = processString(content)
if parts := strings.SplitN(content, "", 2); len(parts) == 2 {
itemInfo := strings.TrimSpace(parts[1])
log.Infof("商品修改信息: %s", itemInfo)
groupConfig.ProductName = itemInfo
cond := builder.NewCond()
cond = cond.And(builder.Eq{"config_id": groupConfig.ConfigID})
err = m.botGroupImpl.UpdateByCond(&cond, groupConfig)
if err != nil {
err = fmt.Errorf("修改失败:%v", err)
return
}
successMsg = "修改成功"
isFinish = true
return
}
return
}
func (m *Macro) ProductList(ctx context.Context, groupConfig *model.AiBotGroupConfig) (successMsg string, err error, isFinish bool) {
if len(groupConfig.ProductName) == 0 {
successMsg = "暂未设置"
} else {
successMsg = groupConfig.ProductName
isFinish = true
}
return
}
func processString(s string) string {
// 替换中文逗号为英文逗号
s = strings.ReplaceAll(s, "", ",")
return string(clearSpacialFormat(s))
}
func clearSpacialFormat(s string) (result []rune) {
//过滤控制字符(如 \n, \t, \r 等)
for _, char := range s {
// 判断是否是控制字符ASCII < 32 或 = 127
if !unicode.IsControl(char) {
// 如果需要完全移除 \n 和 \t可以改成
// if !unicode.IsControl(char)
result = append(result, char)
}
}
return
}
func ParseLossData(input string) (map[int32]*bbxt.ResellerLossSumProductRelation, error) {
result := make(map[int32]*bbxt.ResellerLossSumProductRelation)
// 按行分割
lines := strings.Split(input, "\n")
// 跳过第一行的标题
startIdx := 0
for i, line := range lines {
if strings.HasPrefix(line, "[") && strings.Contains(line, "]") && strings.Contains(line, "->") {
startIdx = i
break
}
}
var currentResellerID int32
var currentReseller *bbxt.ResellerLossSumProductRelation
for i := startIdx; i < len(lines); i++ {
line := strings.TrimSpace(lines[i])
if line == "" {
continue
}
// 检查是否是供应商行(以 [ 开头)
if strings.HasPrefix(line, "[") && strings.Contains(line, "]") {
// 解析供应商行:[25131]兴业银行-营销系统->未查找到对应商务
// 找到第一个 ] 的位置
bracketEnd := strings.Index(line, "]")
if bracketEnd == -1 {
continue
}
// 提取供应商ID
idStr := line[1:bracketEnd]
id, err := strconv.ParseInt(idStr, 10, 32)
if err != nil {
continue
}
currentResellerID = int32(id)
// 提取供应商名称和商务名称
remaining := strings.TrimSpace(line[bracketEnd+1:])
// 分割供应商名称和商务名称
var resellerName, afterSaleName string
arrowIdx := strings.Index(remaining, "->")
if arrowIdx != -1 {
resellerName = strings.TrimSpace(remaining[:arrowIdx])
afterSaleName = strings.TrimSpace(remaining[arrowIdx+2:])
} else {
resellerName = remaining
afterSaleName = ""
}
// 创建新的供应商对象
currentReseller = &bbxt.ResellerLossSumProductRelation{
AfterSaleName: afterSaleName,
ResellerName: resellerName,
Products: make(map[int32]*bbxt.LossReason),
}
result[currentResellerID] = currentReseller
} else if strings.Contains(line, "└──") {
// 解析商品行:└── [460]全国话费30元:未填写
if currentReseller == nil {
continue
}
// 移除└──前缀
productLine := strings.TrimPrefix(line, "└──")
productLine = strings.TrimSpace(productLine)
if !strings.HasPrefix(productLine, "[") {
continue
}
// 找到商品ID的结束位置
prodBracketEnd := strings.Index(productLine, "]")
if prodBracketEnd == -1 {
continue
}
// 提取商品ID
prodIDStr := productLine[1:prodBracketEnd]
prodID, err := strconv.ParseInt(prodIDStr, 10, 32)
if err != nil {
continue
}
// 提取商品名称和亏损原因
prodRemaining := strings.TrimSpace(productLine[prodBracketEnd+1:])
// 找到冒号分隔符
colonIdx := strings.Index(prodRemaining, ":")
var productName, lossReason string
if colonIdx != -1 {
productName = strings.TrimSpace(prodRemaining[:colonIdx])
lossReason = strings.TrimSpace(prodRemaining[colonIdx+1:])
} else {
productName = prodRemaining
lossReason = ""
}
// 添加到当前供应商的商品列表中
currentReseller.Products[int32(prodID)] = &bbxt.LossReason{
ProductName: productName,
LossReason: lossReason,
}
}
}
return result, nil
}