289 lines
8.1 KiB
Go
289 lines
8.1 KiB
Go
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
|
||
}
|