Compare commits

..

No commits in common. "d17e488dd77cf3cb02de405d7464775a5dc5eaa7" and "ebc3f20e0418ecb1eb6311acdfdb314fd2e3b02d" have entirely different histories.

20 changed files with 78 additions and 403 deletions

View File

@ -22,6 +22,12 @@ vllm:
coze: coze:
base_url: "https://api.coze.cn" base_url: "https://api.coze.cn"
redis:
host: 47.97.27.195:6379
type: node
pass:
lansexiongdi@666i_secret: "sat_AqvFcdNgesP8megy1ItTscWFXRcsHRzmM4NJ1KNavfcdT0EPwYuCPkDqGhItpx13"
lsxd: lsxd:
# 统一登录 # 统一登录
login_url: "https://api.user.1688sup.com/v1/login/phone" login_url: "https://api.user.1688sup.com/v1/login/phone"
@ -42,16 +48,6 @@ sys:
maxIdleTime: 30 #每个连接最大空闲时间,如果超过了这个时间会被关闭 maxIdleTime: 30 #每个连接最大空闲时间,如果超过了这个时间会被关闭
tls: 30 tls: 30
db: db:
redis:
host: 47.97.27.195:6379
type: node
pass: lansexiongdi@666
key: report-api-test
pollSize: 5 #连接池大小不配置或配置为0表示不启用连接池
minIdleConns: 2 #最小空闲连接数
maxIdleTime: 30 #每个连接最大空闲时间,如果超过了这个时间会被关闭
tls: 30
db:
db: db:
driver: mysql driver: mysql
source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai source: root:SD###sdf323r343@tcp(121.199.38.107:3306)/sys_ai?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
@ -154,14 +150,14 @@ dingtalk:
sheet_id_or_name: "数据表" sheet_id_or_name: "数据表"
# 机器人群组 # 机器人群组
bot_group_id: bot_group_id:
bbxt: 29 bbxt: 28
qywx: qywx:
corp_id: "ww48151f694fb8ec67" corp_id: "ww48151f694fb8ec67"
app_secret: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk" app_secret: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk"
token: "zJdukry6" token: "Jdukry6"
aes_key: "4VLH47qRGUogc2d3QLWuUhvJlk8Y0YuRjXzeBquBq8B" aes_key: "4VLH47qRGUogc2d3QLWuUhvJlk8Y0YuRjXzeBquBq8B"
init_account: "les.,FuZhongYun" init_account: "les."
chat_id_len: 16 chat_id_len: 16
default_config_id: 1 default_config_id: 1
bot_group_id: bot_group_id:

View File

@ -152,15 +152,20 @@ dingtalk:
bbxt: 23 bbxt: 23
qywx: qywx:
corp_id: "ww48151f694fb8ec67" # corp_id: "ww48151f694fb8ec67"
# app_secret: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk"
# token: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk"
# aes_key: "4VLH47qRGUogc2d3QLWuUhvJlk8Y0YuRjXzeBquBq8B"
corp_id: "wwabfd0cec7171e769"
app_secret: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk" app_secret: "uYqtdwdtdH4Uv_P4is2AChuGzBCoB6cQDyRvpbW0Vmk"
token: "zJdukry6" token: "gY1AGR3mjBhzy"
aes_key: "4VLH47qRGUogc2d3QLWuUhvJlk8Y0YuRjXzeBquBq8B" aes_key: "g8VGfQEqluUhoKOlyjmmll8Q9C5tVFUTX5T2qkmI9Sv"
init_account: "les.,FuZhongYun" init_account: "les."
chat_id_len: 16 chat_id_len: 16
default_config_id: 1 default_config_id: 1
bot_group_id: bot_group_id:
bbxt: 35 bbxt: 23
default_prompt: default_prompt:
img_recognize: img_recognize:

View File

@ -81,7 +81,7 @@ func (g *GroupConfigBiz) GetReportLists(ctx context.Context, groupConfig *model.
if err != nil { if err != nil {
return return
} }
//product = []string{"优酷周卡", "优酷季卡", "优酷年卡", "爱奇艺黄金会员天卡"}
//追加电商充值系统统计 - 返回统一使用[]*bbxt.ReportRes //追加电商充值系统统计 - 返回统一使用[]*bbxt.ReportRes
rechargeReports, err := g.rechargeDailyReport(ctx, time.Now(), nil, g.ossClient) rechargeReports, err := g.rechargeDailyReport(ctx, time.Now(), nil, g.ossClient)
if err != nil || len(rechargeReports) == 0 { if err != nil || len(rechargeReports) == 0 {
@ -118,10 +118,11 @@ func (g *GroupConfigBiz) rechargeDailyReport(ctx context.Context, now time.Time,
reports = []*bbxt.ReportRes{ reports = []*bbxt.ReportRes{
{ {
ReportName: "我们的商品统计(电商充值系统)", ReportName: "我们的商品统计(电商充值系统)",
Title: res["title"].(string), Title: fmt.Sprintf("%s 电商充值系统我们的商品统计", now.Format("2006-01-02")),
Path: res["path"].(string), Path: res["path"].(string),
Url: res["url"].(string), Url: res["url"].(string),
Data: res["data"].([][]string), Data: res["data"].([][]string),
Desc: res["desc"].(string),
}, },
} }
@ -179,11 +180,6 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz
return _err return _err
} }
reports = append(reports, repo...) reports = append(reports, repo...)
rechargeReport, _err := g.rechargeDailyReport(ctx, t, product, nil)
if _err != nil || len(repo) == 0 {
return _err
}
reports = append(reports, rechargeReport...)
case "report_daily_recharge": case "report_daily_recharge":
product := strings.Split(groupConfig.ProductName, ",") product := strings.Split(groupConfig.ProductName, ",")
repo, _err := g.rechargeDailyReport(ctx, t, product, nil) repo, _err := g.rechargeDailyReport(ctx, t, product, nil)
@ -203,9 +199,6 @@ func (g *GroupConfigBiz) handleReport(ctx context.Context, rec *entitys.Recogniz
} }
for _, report := range reports { for _, report := range reports {
if report == nil {
continue
}
err = uploader.Run(report) err = uploader.Run(report)
if err != nil { if err != nil {
log.Error(err) log.Error(err)

View File

@ -80,11 +80,3 @@ func (a *Auth) getNewAccessToken(ctx context.Context, corpid string, corpsecret
} }
return return
} }
type UploadMediaRes struct {
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
Type string `json:"type"`
MediaId string `json:"media_id"`
CreatedAt string `json:"created_at"`
}

View File

@ -33,17 +33,19 @@ func NewGroup(groupImpl *impl.BotGroupQywxImpl, auth *Auth) *Group {
// - GroupCreateResp: 创建群聊的响应结果 // - GroupCreateResp: 创建群聊的响应结果
// - error: 错误信息,如果请求失败则返回错误 // - error: 错误信息,如果请求失败则返回错误
func (g *Group) Create(ctx context.Context, req GroupCreateReq, corpid string, corpsecret string) (GroupCreateResp, error) { func (g *Group) Create(ctx context.Context, req GroupCreateReq, corpid string, corpsecret string) (GroupCreateResp, error) {
// 声明一个GroupCreateResp结构体变量res用于存储响应结果
var res GroupCreateResp var res GroupCreateResp
// 将请求结构体req转换为map类型的参数param
// 如果转换失败,忽略错误
param, _ := util.StructToMap(req) param, _ := util.StructToMap(req)
// 发送HTTP请求到企业微信API创建群聊
// 参数依次为上下文、请求参数、请求URL、响应结果存储指针、企业ID、应用密钥
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/create", &res, corpid, corpsecret) _, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/create", &res, corpid, corpsecret)
// 如果请求过程中发生错误,返回响应结果和错误
if err != nil { if err != nil {
return res, err return res, err
} }
// 请求成功返回响应结果和nil错误
return res, nil return res, nil
} }
@ -58,76 +60,18 @@ func (g *Group) Create(ctx context.Context, req GroupCreateReq, corpid string, c
// - error: 操作过程中发生的错误如果成功则为nil // - error: 操作过程中发生的错误如果成功则为nil
func (g *Group) SendMarkDown(ctx context.Context, req GroupSendMarkDownReq, corpid string, corpsecret string) error { func (g *Group) SendMarkDown(ctx context.Context, req GroupSendMarkDownReq, corpid string, corpsecret string) error {
req.Msgtype = "markdown_v2" // 设置消息类型为Markdown
req.Msgtype = "markdown"
// 将请求结构体转换为map类型便于后续的HTTP请求参数处理
param, _ := util.StructToMap(req) param, _ := util.StructToMap(req)
// 调用request方法发送HTTP请求到企业微信API
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret) // 参数依次为:上下文、请求参数、API URL、额外请求头、corpid、corpsecret
_, err := g.request(ctx, param, " https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret)
// 如果请求过程中发生错误,直接返回错误
if err != nil { if err != nil {
return err return err
} }
// 请求成功返回nil
return nil
}
func (g *Group) SendNews(ctx context.Context, req GroupSendNewsReq, corpid string, corpsecret string) error {
req.Msgtype = "news"
param, _ := util.StructToMap(req)
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret)
if err != nil {
return err
}
return nil
}
func (g *Group) SendImg(ctx context.Context, req GroupSendImgReq, corpid string, corpsecret string) error {
req.Msgtype = "image"
param, _ := util.StructToMap(req)
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret)
if err != nil {
return err
}
return nil
}
func (g *Group) SendMpNews(ctx context.Context, req GroupSendMpNewsReq, corpid string, corpsecret string) error {
req.Msgtype = "mpnews"
param, _ := util.StructToMap(req)
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret)
if err != nil {
return err
}
return nil
}
func (g *Group) SendText(ctx context.Context, req GroupSendTextReq, corpid string, corpsecret string) error {
req.Msgtype = "text"
param, _ := util.StructToMap(req)
_, err := g.request(ctx, param, "https://qyapi.weixin.qq.com/cgi-bin/appchat/send", nil, corpid, corpsecret)
if err != nil {
return err
}
return nil return nil
} }

View File

@ -1,103 +0,0 @@
package qywx
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"path"
"time"
)
type Other struct {
auth *Auth
}
func NewOther(auth *Auth) *Other {
return &Other{
auth: auth,
}
}
func (g *Other) UploadMediaWithUrl(ctx context.Context, fileUrl, mediaType, corpid, corpsecret string) (uploadRes *UploadMediaRes, err error) {
// 1. 获取AccessToken
auth, err := g.auth.GetAccessToken(ctx, corpid, corpsecret)
if err != nil {
return nil, fmt.Errorf("获取AccessToken失败: %v", err)
}
// 2. 下载文件
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(fileUrl)
if err != nil {
return nil, fmt.Errorf("下载文件失败: %v", err)
}
defer resp.Body.Close()
// 检查响应状态
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("下载文件状态码错误: %d", resp.StatusCode)
}
// 3. 准备上传请求修正点使用正确的上传API
uploadUrl := fmt.Sprintf("https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s",
auth.AccessToken, mediaType)
// 4. 创建multipart表单
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 从URL提取文件名或使用默认名
filename := path.Base(fileUrl)
if filename == "/" || filename == "." {
filename = fmt.Sprintf("file_%d.dat", time.Now().Unix())
}
part, err := writer.CreateFormFile("media", filename)
if err != nil {
return nil, fmt.Errorf("创建表单文件失败: %v", err)
}
// 5. 流式传输文件内容
if _, err = io.Copy(part, resp.Body); err != nil {
return nil, fmt.Errorf("写入文件内容失败: %v", err)
}
// 6. 关闭writer
if err = writer.Close(); err != nil {
return nil, fmt.Errorf("关闭writer失败: %v", err)
}
// 7. 发送上传请求
req, err := http.NewRequest("POST", uploadUrl, body)
if err != nil {
return nil, fmt.Errorf("创建请求失败: %v", err)
}
req.Header.Set("Content-Type", writer.FormDataContentType())
uploadResp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("上传请求失败: %v", err)
}
defer uploadResp.Body.Close()
// 8. 解析响应
respBody, err := io.ReadAll(uploadResp.Body)
if err != nil {
return nil, fmt.Errorf("读取响应失败: %v", err)
}
if err = json.Unmarshal(respBody, &uploadRes); err != nil {
return nil, fmt.Errorf("解析响应失败: %v", err)
}
if uploadRes.Errcode != 0 {
return nil, fmt.Errorf("上传失败 [code=%d]: %s", uploadRes.Errcode, uploadRes.Errmsg)
}
return
}

View File

@ -37,66 +37,9 @@ type commonResp struct {
type GroupSendMarkDownReq struct { type GroupSendMarkDownReq struct {
Chatid string `json:"chatid"` Chatid string `json:"chatid"`
Msgtype string `json:"msgtype"` Msgtype string `json:"msgtype"`
Markdown MarkDown `json:"markdown_v2"` Markdown MarkDown `json:"markdown"`
} }
type MarkDown struct { type MarkDown struct {
Content string `json:"content"` Content string `json:"content"`
} }
type GroupSendNewsReq struct {
Chatid string `json:"chatid"`
Msgtype string `json:"msgtype"`
News News `json:"news"`
Safe int `json:"safe"`
}
type News struct {
Articles []Articles `json:"articles"`
}
type Articles struct {
Title string `json:"title"`
Description string `json:"description"`
Url string `json:"url"`
Picurl string `json:"picurl"`
}
type GroupSendImgReq struct {
Chatid string `json:"chatid"`
Msgtype string `json:"msgtype"`
Image Image `json:"image"`
Safe int `json:"safe"`
}
type Image struct {
MediaId string `json:"media_id"`
}
type GroupSendMpNewsReq struct {
Chatid string `json:"chatid"`
Msgtype string `json:"msgtype"`
Mpnews Mpnews `json:"mpnews"`
Safe int `json:"safe"`
}
type Mpnews struct {
Articles []ArticlesMpnews `json:"articles"`
}
type ArticlesMpnews struct {
Title string `json:"title"`
ThumbMediaId string `json:"thumb_media_id"`
Author string `json:"author"`
ContentSourceUrl string `json:"content_source_url"`
Content string `json:"content"`
Digest string `json:"digest"`
}
type GroupSendTextReq struct {
Chatid string `json:"chatid"`
Msgtype string `json:"msgtype"`
Text Text `json:"text"`
Safe int `json:"safe"`
}
type Text struct {
Content string `json:"content"`
}

View File

@ -8,7 +8,7 @@ import (
"ai_scheduler/internal/pkg" "ai_scheduler/internal/pkg"
"ai_scheduler/internal/tools/bbxt" "ai_scheduler/internal/tools/bbxt"
"context" "context"
"strings" "fmt"
"time" "time"
"ai_scheduler/internal/config" "ai_scheduler/internal/config"
@ -21,7 +21,6 @@ type QywxAppBiz struct {
conf *config.Config conf *config.Config
botGroupQywxImpl *impl.BotGroupQywxImpl botGroupQywxImpl *impl.BotGroupQywxImpl
qywxGroupHandle *qywx.Group qywxGroupHandle *qywx.Group
qywxOtherHandle *qywx.Other
} }
// NewDingTalkBotBiz // NewDingTalkBotBiz
@ -29,13 +28,11 @@ func NewQywxAppBiz(
conf *config.Config, conf *config.Config,
botGroupQywxImpl *impl.BotGroupQywxImpl, botGroupQywxImpl *impl.BotGroupQywxImpl,
qywxGroupHandle *qywx.Group, qywxGroupHandle *qywx.Group,
qywxOtherHandle *qywx.Other,
) *QywxAppBiz { ) *QywxAppBiz {
return &QywxAppBiz{ return &QywxAppBiz{
conf: conf, conf: conf,
botGroupQywxImpl: botGroupQywxImpl, botGroupQywxImpl: botGroupQywxImpl,
qywxGroupHandle: qywxGroupHandle, qywxGroupHandle: qywxGroupHandle,
qywxOtherHandle: qywxOtherHandle,
} }
} }
@ -56,7 +53,9 @@ func (q *QywxAppBiz) InitGroup(ctx context.Context) (string, error) {
qywx.GroupCreateReq{ qywx.GroupCreateReq{
Name: GroupInfo.Title, Name: GroupInfo.Title,
Chatid: GroupInfo.ChatID, Chatid: GroupInfo.ChatID,
Userlist: strings.Split(q.conf.Qywx.InitAccount, ","), Userlist: []string{
q.conf.Qywx.InitAccount,
},
}, },
q.conf.Qywx.CorpId, q.conf.Qywx.CorpId,
GroupInfo.AppSecret, GroupInfo.AppSecret,
@ -78,30 +77,12 @@ func (q *QywxAppBiz) GetGroupInfo(ctx context.Context, groupId int) (group model
} }
func (q *QywxAppBiz) SendReport(ctx context.Context, groupInfo *model.AiBotGroupQywx, report *bbxt.ReportRes) (err error) { func (q *QywxAppBiz) SendReport(ctx context.Context, groupInfo *model.AiBotGroupQywx, report *bbxt.ReportRes) (err error) {
//confitent := fmt.Sprintf("%s\n%s", report.Title, fmt.Sprintf("![图片](%s)", report.Url)) confitent := fmt.Sprintf("%s\n%s", report.Title, fmt.Sprintf("![图片](%s)", report.Url))
err = q.qywxGroupHandle.SendMarkDown(ctx, qywx.GroupSendMarkDownReq{
upload, err := q.qywxOtherHandle.UploadMediaWithUrl(ctx, report.Url, "image", q.conf.Qywx.CorpId, groupInfo.AppSecret)
if err != nil {
return
}
err = q.qywxGroupHandle.SendText(ctx, qywx.GroupSendTextReq{
Chatid: groupInfo.ChatID, Chatid: groupInfo.ChatID,
Text: qywx.Text{ Markdown: qywx.MarkDown{
Content: report.ReportName + "\n" + report.Title, Content: confitent,
}, },
}, q.conf.Qywx.CorpId, groupInfo.AppSecret) }, q.conf.Qywx.CorpId, groupInfo.AppSecret)
if err != nil {
return
}
err = q.qywxGroupHandle.SendImg(ctx, qywx.GroupSendImgReq{
Chatid: groupInfo.ChatID,
Image: qywx.Image{
MediaId: upload.MediaId,
},
}, q.conf.Qywx.CorpId, groupInfo.AppSecret)
if err != nil {
return
}
return return
} }

View File

@ -15,7 +15,6 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"path/filepath" "path/filepath"
"sort"
"strconv" "strconv"
"time" "time"
@ -92,7 +91,6 @@ type StatisticsOursProductContext struct {
ProductData []statistics_ours_product.StatisticsOursProductItem ProductData []statistics_ours_product.StatisticsOursProductItem
ImgUrl string ImgUrl string
ExcelData [][]string ExcelData [][]string
TotalLoss float64
} }
func (w *statisticsOursProduct) buildWorkflow(ctx context.Context) (compose.Runnable[*StatisticsOursProductWorkflowInput, map[string]any], error) { func (w *statisticsOursProduct) buildWorkflow(ctx context.Context) (compose.Runnable[*StatisticsOursProductWorkflowInput, map[string]any], error) {
@ -124,7 +122,7 @@ func (w *statisticsOursProduct) formatContext(ctx context.Context, input *Statis
Time: time.Now(), Time: time.Now(),
StartTime: startTime, StartTime: startTime,
EndTime: endTime, EndTime: endTime,
Title: fmt.Sprintf("截止 %s 亏损100以上我们的商品统计", endTimeStr), Title: fmt.Sprintf("截止 %s 我们的商品统计", endTimeStr),
}, nil }, nil
} }
@ -164,7 +162,7 @@ func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, stat
fileName := fmt.Sprintf("statistics_ours_product_%d%d", time.Now().Unix(), rand.Intn(1000)) fileName := fmt.Sprintf("statistics_ours_product_%d%d", time.Now().Unix(), rand.Intn(1000))
// 2. 转换数据为 [][]string // 2. 转换数据为 [][]string
excelData, totalLoss := w.convertDataToExcelFormat(state.ProductData) excelData := w.convertDataToExcelFormat(state.ProductData)
// 3. 生成 Excel // 3. 生成 Excel
req := &excel_generator.ExcelGeneratorRequest{ req := &excel_generator.ExcelGeneratorRequest{
@ -193,20 +191,13 @@ func (w *statisticsOursProduct) generateExcelAndUpload(ctx context.Context, stat
state.ImgUrl = url state.ImgUrl = url
state.ExcelData = excelData state.ExcelData = excelData
state.TotalLoss = totalLoss
return state, nil return state, nil
} }
// convertDataToExcelFormat 将业务数据转换为 Excel 生成器需要的二维字符串数组 // convertDataToExcelFormat 将业务数据转换为 Excel 生成器需要的二维字符串数组
func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_product.StatisticsOursProductItem) ([][]string, float64) { func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_product.StatisticsOursProductItem) [][]string {
type sortType struct { var result [][]string
Profit float64
cells []string
}
var sortList []sortType
var totalLoss float64
for _, item := range data { for _, item := range data {
var profitVal float64 var profitVal float64
@ -228,11 +219,6 @@ func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_
profitVal = 0 profitVal = 0
} }
// 累加总亏损
if profitVal < 0 {
totalLoss += profitVal
}
// 过滤利润小于 -100 的记录 // 过滤利润小于 -100 的记录
if profitVal > -100 { if profitVal > -100 {
continue continue
@ -250,24 +236,9 @@ func (w *statisticsOursProduct) convertDataToExcelFormat(data []statistics_ours_
fmt.Sprintf("%v", item.Profit), fmt.Sprintf("%v", item.Profit),
} }
sortList = append(sortList, sortType{ result = append(result, row)
Profit: profitVal,
cells: row,
})
} }
return result
// 排序
sort.Slice(sortList, func(i, j int) bool {
return sortList[i].Profit < sortList[j].Profit
})
// 转换为 [][]string
result := make([][]string, 0, len(sortList))
for _, item := range sortList {
result = append(result, item.cells)
}
return result, totalLoss
} }
func (w *statisticsOursProduct) convertToMap(ctx context.Context, state *StatisticsOursProductContext) (map[string]any, error) { func (w *statisticsOursProduct) convertToMap(ctx context.Context, state *StatisticsOursProductContext) (map[string]any, error) {
@ -275,6 +246,6 @@ func (w *statisticsOursProduct) convertToMap(ctx context.Context, state *Statist
"path": "", "path": "",
"url": state.ImgUrl, "url": state.ImgUrl,
"data": state.ExcelData, "data": state.ExcelData,
"title": state.Title + fmt.Sprintf("(总亏损 %.2f", state.TotalLoss), "desc": state.Title,
}, nil }, nil
} }

View File

@ -8,8 +8,6 @@ import (
"net/url" "net/url"
"strings" "strings"
"time" "time"
"github.com/go-kratos/kratos/v2/log"
) )
// 请求结构体 // 请求结构体
@ -110,11 +108,7 @@ func (r *Request) prepare() *http.Request {
Method := r.getMethod() Method := r.getMethod()
Url := r.getUrl() Url := r.getUrl()
Data := r.getData() Data := r.getData()
req, err := http.NewRequest(Method, Url, Data) req, _ := http.NewRequest(Method, Url, Data)
if err != nil {
log.Error(err)
return nil
}
r.addHeaders(req) r.addHeaders(req)
return req return req
} }

View File

@ -44,11 +44,6 @@ func (c *CronServer) InitJobs(ctx context.Context) {
Name: "直连天下报表推送", Name: "直连天下报表推送",
Schedule: "0 12,18,23 * * *", Schedule: "0 12,18,23 * * *",
}, },
{
Func: c.cronService.CronReportSendQywx,
Name: "直连天下报表推送",
Schedule: "0 12,18,23 * * *",
},
} }
} }

View File

@ -129,9 +129,7 @@ func registerResponse(router fiber.Router) {
func registerCommon(c *fiber.Ctx, err error) error { func registerCommon(c *fiber.Ctx, err error) error {
// 调用下一个中间件或路由处理函数 // 调用下一个中间件或路由处理函数
if c.Path() == "/api/v1/qywx/callback" {
return nil
}
bsErr, ok := err.(*errors.BusinessErr) bsErr, ok := err.(*errors.BusinessErr)
if !ok { if !ok {
bsErr = errors.SystemError bsErr = errors.SystemError

View File

@ -332,21 +332,14 @@ func (s *CallbackService) handleBugOptimizationSubmitDone(ctx context.Context, t
return msg, nil return msg, nil
} }
func (s *CallbackService) QywxCallback(c *fiber.Ctx) (err error) { func (s *CallbackService) QywxCallback(c *fiber.Ctx) error {
// 读取头 // 读取头
httpstr := string(c.Request().URI().QueryString()) msgSignature := c.Query("msg_signature")
timestamp := c.Query("timestamp")
start := strings.Index(httpstr, "msg_signature=") nonce := c.Query("nonce")
start += len("msg_signature=") echostr := c.Query("echostr")
var msgSignature string
next := getString(httpstr, "&timestamp=", start, &msgSignature)
var timestamp string
next = getString(httpstr, "&nonce=", next, &timestamp)
var nonce string
next = getString(httpstr, "&echostr=", next, &nonce)
echostr := httpstr[next:len(httpstr)]
echostr, _ = url.QueryUnescape(echostr) echostr, _ = url.QueryUnescape(echostr)
fmt.Println(httpstr, msgSignature, timestamp, nonce, echostr) fmt.Println(msgSignature, timestamp, nonce, echostr)
wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(s.cfg.Qywx.Token, s.cfg.Qywx.AES_KEY, s.cfg.Qywx.CorpId, wxbizjsonmsgcrypt.JsonType) wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(s.cfg.Qywx.Token, s.cfg.Qywx.AES_KEY, s.cfg.Qywx.CorpId, wxbizjsonmsgcrypt.JsonType)
echoStr, cryptErr := wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr) echoStr, cryptErr := wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr)
if nil != cryptErr { if nil != cryptErr {
@ -354,13 +347,7 @@ func (s *CallbackService) QywxCallback(c *fiber.Ctx) (err error) {
return fmt.Errorf("%v", cryptErr) return fmt.Errorf("%v", cryptErr)
} }
fmt.Println("verifyUrl success echoStr", string(echoStr)) fmt.Println("verifyUrl success echoStr", string(echoStr))
err = c.Send(echoStr) err := c.Send(echoStr)
return err return err
} }
func getString(str, endstr string, start int, msg *string) int {
end := strings.Index(str, endstr)
*msg = str[start:end]
return end + len(endstr)
}

View File

@ -86,7 +86,7 @@ func (h *ChatService) Chat(c *websocket.Conn) {
} }
// 开启心跳检测 // 开启心跳检测
go client.InitHeartbeat(time.Duration(h.cfg.Sys.HeartbeatInterval)) //go client.InitHeartbeat(time.Duration(h.cfg.Sys.HeartbeatInterval))
// 循环读取客户端消息 // 循环读取客户端消息
for { for {

View File

@ -35,16 +35,10 @@ func (d *CronService) CronReportSendDingTalk(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
if groupInfo.GroupID == 0 {
return nil
}
groupConfig, err := d.groupConfigBiz.GetGroupConfig(ctx, groupInfo.ConfigID) groupConfig, err := d.groupConfigBiz.GetGroupConfig(ctx, groupInfo.ConfigID)
if err != nil { if err != nil {
return err return err
} }
if groupConfig.ConfigID == 0 {
return nil
}
reports, err := d.groupConfigBiz.GetReportLists(ctx, groupConfig) reports, err := d.groupConfigBiz.GetReportLists(ctx, groupConfig)
if err != nil { if err != nil {
return err return err
@ -66,16 +60,10 @@ func (d *CronService) CronReportSendQywx(ctx context.Context) error {
if err != nil { if err != nil {
return err return err
} }
if groupInfo.GroupID == 0 {
return nil
}
groupConfig, err := d.groupConfigBiz.GetGroupConfig(ctx, groupInfo.ConfigID) groupConfig, err := d.groupConfigBiz.GetGroupConfig(ctx, groupInfo.ConfigID)
if err != nil { if err != nil {
return err return err
} }
if groupConfig.ConfigID == 0 {
return nil
}
reports, err := d.groupConfigBiz.GetReportLists(ctx, groupConfig) reports, err := d.groupConfigBiz.GetReportLists(ctx, groupConfig)
if err != nil { if err != nil {
return err return err

View File

@ -117,8 +117,7 @@ func run() {
botGroupQywxImpl := impl.NewBotGroupQywxImpl(db) botGroupQywxImpl := impl.NewBotGroupQywxImpl(db)
qywxAuth := qywx.NewAuth(configConfig, rdb) qywxAuth := qywx.NewAuth(configConfig, rdb)
group := qywx.NewGroup(botGroupQywxImpl, qywxAuth) group := qywx.NewGroup(botGroupQywxImpl, qywxAuth)
other := qywx.NewOther(qywxAuth) qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group)
qywxAppBiz := biz.NewQywxAppBiz(configConfig, botGroupQywxImpl, group, other)
groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig) groupConfigBiz := biz.NewGroupConfigBiz(toolRegis, utils_ossClient, botGroupConfigImpl, registry, configConfig)
dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, manager, configConfig, sendCardClient, groupConfigBiz) dingTalkBotBiz := biz.NewDingTalkBotBiz(doDo, handle, botConfigImpl, botGroupImpl, user, botChatHisImpl, manager, configConfig, sendCardClient, groupConfigBiz)
// 初始化钉钉机器人服务 // 初始化钉钉机器人服务

View File

@ -355,20 +355,19 @@ func (b *BbxtTools) GetStatisOfficialProductSumDecline(now time.Time, downWardVa
return return
} }
var ( var (
productSumMap = make(map[string]ProductSumDecline) productSumMap = make(map[int32]ProductSumDecline)
) )
for _, v := range data.OfficialProductSumDecline { for _, v := range data.OfficialProductSumDecline {
if _, ex := productSumMap[v.OfficialProductName]; !ex { if _, ex := productSumMap[v.OfficialProductId]; !ex {
productSumMap[v.OfficialProductName] = ProductSumDecline{ productSumMap[v.OfficialProductId] = ProductSumDecline{
OfficialProductName: v.OfficialProductName, OfficialProductName: v.OfficialProductName,
OfficialProductId: v.OfficialProductId, OfficialProductId: v.OfficialProductId,
ProductSumReseller: make(map[int32]ProductSumReseller), ProductSumReseller: make(map[int32]ProductSumReseller),
Index: productMap[v.OfficialProductName],
} }
} }
if v.HistoryOneDiff <= sumFilter || v.HistoryTwoDiff <= sumFilter { if v.HistoryOneDiff <= sumFilter || v.HistoryTwoDiff <= sumFilter {
productSumMap[v.OfficialProductName].ProductSumReseller[v.ResellerId] = ProductSumReseller{ productSumMap[v.OfficialProductId].ProductSumReseller[v.ResellerId] = ProductSumReseller{
ResellerName: v.ResellerName, ResellerName: v.ResellerName,
CurrentNum: v.CurrentNum, CurrentNum: v.CurrentNum,
HistoryOneNum: v.HistoryOneNum, HistoryOneNum: v.HistoryOneNum,
@ -377,14 +376,8 @@ func (b *BbxtTools) GetStatisOfficialProductSumDecline(now time.Time, downWardVa
HistoryTwoDiff: v.HistoryTwoDiff, HistoryTwoDiff: v.HistoryTwoDiff,
} }
} }
} }
var total = make([]ProductSumDecline, 0, len(productSumMap))
for k, _ := range productSumMap {
total = append(total, productSumMap[k])
}
sort.Slice(total, func(i, j int) bool {
return total[i].Index > total[j].Index
})
timeCh := now.Format("1月2日15点") timeCh := now.Format("1月2日15点")
//title := "截至" + timeCh + "销量下滑大于" + fmt.Sprintf("%d", downWardValue) + "明细,分销商仅展示差额大于" + fmt.Sprintf("%d", -sumFilter) //title := "截至" + timeCh + "销量下滑大于" + fmt.Sprintf("%d", downWardValue) + "明细,分销商仅展示差额大于" + fmt.Sprintf("%d", -sumFilter)
title := "截至" + timeCh + "销量下滑较大商品" title := "截至" + timeCh + "销量下滑较大商品"
@ -393,7 +386,7 @@ func (b *BbxtTools) GetStatisOfficialProductSumDecline(now time.Time, downWardVa
return return
} }
filePath := b.cacheDir + "/xlxhmx" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx" filePath := b.cacheDir + "/xlxhmx" + fmt.Sprintf("%d%d", time.Now().Unix(), rand.Intn(1000)) + ".xlsx"
err = b.OfficialProductSumDeclineExcel(b.excelTempDir+"/"+"/xlxhmx.xlsx", filePath, total, title) err = b.OfficialProductSumDeclineExcel(b.excelTempDir+"/"+"/xlxhmx.xlsx", filePath, productSumMap, title)
return &ReportRes{ return &ReportRes{
ReportName: "销售下滑明细", ReportName: "销售下滑明细",
Title: title, Title: title,

View File

@ -60,7 +60,7 @@ func Test_GetStatisOfficialProductSumDecline(t *testing.T) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
s := "官方--腾讯-周卡,官方--腾讯-月卡,官方--腾讯-季卡,官方--腾讯-年卡,官方--优酷周卡,官方--优酷月卡,官方--优酷季卡,官方--优酷年卡,官方--爱奇艺-周卡,官方--爱奇艺-月卡,官方--爱奇艺-季卡,官方--爱奇艺-年卡,官方--芒果-PC周卡,官方--芒果-PC月卡,官方--芒果-PC季卡,官方--美团外卖红包5元,官方--美团外卖红包10元,官方--QQ音乐-绿钻月卡,官方--饿了么超级会员月卡,官方--网易云黑胶vip月卡,官方--喜马拉雅巅峰会员月卡" s := "官方--美团外卖红包5元,官方--美团外卖红包10元,官方--饿了么超级会员月卡,官方--网易云黑胶vip月卡,官方--喜马拉雅巅峰会员月卡,官方--芒果-PC季卡,官方--芒果-PC月卡,官方--芒果-PC周卡,官方--腾讯-周卡,官方--优酷周卡,官方--QQ音乐-绿钻月卡,官方--爱奇艺-周卡,官方--腾讯-月卡,官方--腾讯-季卡,官方--腾讯-年卡,官方--优酷月卡,官方--优酷季卡,官方--优酷年卡,官方--爱奇艺-月卡,官方--爱奇艺-季卡,官方--爱奇艺-年卡"
//s := "官方--QQ音乐-绿钻月卡" //s := "官方--QQ音乐-绿钻月卡"
report, err := o.GetStatisOfficialProductSumDecline(time.Now(), 1000, strings.Split(s, ","), -150) report, err := o.GetStatisOfficialProductSumDecline(time.Now(), 1000, strings.Split(s, ","), -150)

View File

@ -26,7 +26,6 @@ type ProductSumDecline struct {
OfficialProductId int32 OfficialProductId int32
OfficialProductName string OfficialProductName string
ProductSumReseller map[int32]ProductSumReseller ProductSumReseller map[int32]ProductSumReseller
Index int
} }
type ProductSumReseller struct { type ProductSumReseller struct {

View File

@ -176,7 +176,7 @@ func (u *Uploader) uploadToOSS(fileName string, fileBytes []byte) string {
// return url // return url
//} //}
func (b *BbxtTools) OfficialProductSumDeclineExcel(templatePath, outputPath string, sumSlice []ProductSumDecline, title string) error { func (b *BbxtTools) OfficialProductSumDeclineExcel(templatePath, outputPath string, sumMap map[int32]ProductSumDecline, title string) error {
// 1. 读取模板 // 1. 读取模板
f, err := excelize.OpenFile(templatePath) f, err := excelize.OpenFile(templatePath)
if err != nil { if err != nil {
@ -230,7 +230,7 @@ func (b *BbxtTools) OfficialProductSumDeclineExcel(templatePath, outputPath stri
currentRow := 3 currentRow := 3
pattern := `\$\{(.*?)\}` pattern := `\$\{(.*?)\}`
re := regexp.MustCompile(pattern) re := regexp.MustCompile(pattern)
for _, product := range sumSlice { for _, product := range sumMap {
// 排序 ProductLoss // 排序 ProductLoss
var reseller []ProductSumReseller var reseller []ProductSumReseller
for _, p := range product.ProductSumReseller { for _, p := range product.ProductSumReseller {