Răsfoiți Sursa

临时提交

boweniac 3 luni în urmă
părinte
comite
9b2eb1e583

+ 300 - 164
crontask/compute_statistic.go

@@ -5,6 +5,8 @@ import (
 	"time"
 	"wechat-api/ent"
 	"wechat-api/ent/contact"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/labelrelationship"
 	"wechat-api/ent/messagerecords"
 	"wechat-api/ent/usagedetail"
 	"wechat-api/ent/usagestatisticday"
@@ -23,6 +25,12 @@ func (l *CronTask) computeStatistic() {
 		return
 	}
 
+	wxbotsSet := make(map[uint64][]*ent.Wx)
+	for _, bot := range wxbots {
+		wxbotsSet[bot.OrganizationID] = append(wxbotsSet[bot.OrganizationID], bot)
+	}
+	LabelsCountSet := make(map[uint64][]custom_types.LabelDist)
+
 	/*
 		计算本小时的数据
 		1. 查询出上小时里所有 usagedetail 内容
@@ -42,104 +50,146 @@ func (l *CronTask) computeStatistic() {
 	lastHour := currentHour.Add(-time.Hour * 1)
 	lastHourInt, _ := strconv.Atoi(lastHour.Format("2006010215"))
 
-	for _, wxinfo := range wxbots {
-		l.Logger.Infof("开始计算小时数据:%d\n", currentHourInt)
-
-		// 先判断该账号是否已经统计了小时数据,如果已经统计了,就不需要再统计了
-		var aiResponseInt, sopRunInt, friendCountInt, groupCountInt, accountBalanceInt, consumeTokenInt, activeUserInt, newUserInt int
+	for orgID, wxinfos := range wxbotsSet {
+		var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt, orgNewUserInt int
+		for _, wxinfo := range wxinfos {
+			l.Logger.Infof("开始计算小时数据:%d\n", currentHourInt)
+
+			// 先判断该账号是否已经统计了小时数据,如果已经统计了,就不需要再统计了
+			var aiResponseInt, sopRunInt, friendCountInt, groupCountInt, accountBalanceInt, consumeTokenInt, activeUserInt, newUserInt int
+			hourDataCount, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
+				usagestatistichour.Type(1),
+				usagestatistichour.BotID(wxinfo.Wxid),
+				usagestatistichour.Addtime(uint64(currentHourInt)),
+			).Count(l.ctx)
+			if hourDataCount > 0 {
+				continue
+			}
+
+			// AI回复包括:SOP次数+AI次数
+			// SOP次数:content 非空,source_type = 3 或 4,sub_source_id = 0
+			// AI次数:app = 1 或 3
+			sopresp, _ := l.svcCtx.DB.MessageRecords.Query().Where(
+				messagerecords.SubSourceID(0),
+				messagerecords.SourceTypeIn(3, 4),
+				messagerecords.BotWxid(wxinfo.Wxid),
+				messagerecords.CreatedAtGTE(lastHour),
+				messagerecords.CreatedAtLT(currentHour),
+			).Count(l.ctx)
+			airesp, _ := l.svcCtx.DB.UsageDetail.Query().Where(
+				usagedetail.AppIn(1, 3),
+				usagedetail.BotID(wxinfo.Wxid),
+				usagedetail.CreatedAtGTE(lastHour),
+				usagedetail.CreatedAtLT(currentHour),
+			).Count(l.ctx)
+			aiResponseInt = sopresp + airesp
+			orgAiResponseInt += aiResponseInt
+
+			// SOP执行次数:SOP阶段和节点的执行次数。
+			sopRunInt, _ = l.svcCtx.DB.MessageRecords.Query().Where(
+				messagerecords.BotWxid(wxinfo.Wxid),
+				messagerecords.SubSourceIDEQ(0),
+				messagerecords.SourceTypeIn(3, 4),
+				messagerecords.BotWxid(wxinfo.Wxid),
+				messagerecords.CreatedAtGTE(lastHour),
+				messagerecords.CreatedAtLT(currentHour),
+			).Count(l.ctx)
+			orgSopRunInt += sopRunInt
+
+			// 好友总数:contact 表中 type=1
+			friendCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
+				contact.Type(1),
+				contact.WxWxid(wxinfo.Wxid),
+			).Count(l.ctx)
+			orgFriendCountInt += friendCountInt
+
+			// 群总数:contact 表中 type=2
+			groupCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
+				contact.Type(2),
+				contact.WxWxid(wxinfo.Wxid),
+			).Count(l.ctx)
+			orgGroupCountInt += groupCountInt
+
+			// 消耗Token数:usage_detail 表
+			consumeTokenInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
+				usagedetail.TypeEQ(1),
+				usagedetail.BotID(wxinfo.Wxid),
+				usagedetail.CreatedAtGTE(lastHour),
+				usagedetail.CreatedAtLT(currentHour),
+			).Aggregate(ent.Sum("total_tokens")).Int(l.ctx)
+			orgConsumeTokenInt += consumeTokenInt
+
+			// 账户余额
+			accountBalanceInt = 0
+			orgAccountBalanceInt = 0
+
+			// 活跃好友:usage_detail 表 type = 1
+			activeUserInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
+				usagedetail.Type(1),
+				usagedetail.BotID(wxinfo.Wxid),
+				usagedetail.CreatedAtGTE(lastHour),
+				usagedetail.CreatedAtLT(currentHour),
+			).GroupBy(usagedetail.FieldBotID).Int(l.ctx)
+			orgActiveUserInt += activeUserInt
+
+			lastHourData, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
+				usagestatistichour.AddtimeEQ(uint64(lastHourInt)),
+				usagestatistichour.Type(1),
+				usagestatistichour.BotID(wxinfo.Wxid),
+			).First(l.ctx)
+
+			if lastHourData == nil {
+				newUserInt = friendCountInt
+			} else {
+				newUserInt = int(lastHourData.TotalFriend) - friendCountInt
+			}
+			orgNewUserInt += newUserInt
+
+			_, err := l.svcCtx.DB.UsageStatisticHour.Create().
+				SetType(1).
+				SetBotID(wxinfo.Wxid).
+				SetOrganizationID(wxinfo.OrganizationID).
+				SetAiResponse(uint64(aiResponseInt)).
+				SetSopRun(uint64(sopRunInt)).
+				SetTotalFriend(uint64(friendCountInt)).
+				SetTotalGroup(uint64(groupCountInt)).
+				SetAccountBalance(uint64(accountBalanceInt)).
+				SetConsumeToken(uint64(consumeTokenInt)).
+				SetActiveUser(uint64(activeUserInt)).
+				SetNewUser(int64(newUserInt)).
+				SetAddtime(uint64(currentHourInt)).
+				Save(l.ctx)
+			l.Errorf("save hour data error:%v \n", err)
+		}
+		// 先判断该租户是否已经统计了小时数据,如果已经统计了,就不需要再统计了
 		hourDataCount, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
 			usagestatistichour.Type(1),
-			usagestatistichour.BotID(wxinfo.Wxid),
+			usagestatistichour.OrganizationID(orgID),
+			usagestatistichour.BotIDIsNil(),
 			usagestatistichour.Addtime(uint64(currentHourInt)),
 		).Count(l.ctx)
 		if hourDataCount > 0 {
 			continue
 		}
 
-		// AI回复包括:SOP次数+AI次数
-		// SOP次数:content 非空,source_type = 3 或 4,sub_source_id = 0
-		// AI次数:app = 1 或 3
-		sopresp, _ := l.svcCtx.DB.MessageRecords.Query().Where(
-			messagerecords.SubSourceID(0),
-			messagerecords.SourceTypeIn(3, 4),
-			messagerecords.BotWxid(wxinfo.Wxid),
-			messagerecords.CreatedAtGTE(lastHour),
-			messagerecords.CreatedAtLT(currentHour),
-		).Count(l.ctx)
-		airesp, _ := l.svcCtx.DB.UsageDetail.Query().Where(
-			usagedetail.AppIn(1, 3),
-			usagedetail.BotID(wxinfo.Wxid),
-			usagedetail.CreatedAtGTE(lastHour),
-			usagedetail.CreatedAtLT(currentHour),
-		).Count(l.ctx)
-		aiResponseInt = sopresp + airesp
-
-		// SOP执行次数:SOP阶段和节点的执行次数。
-		sopRunInt, _ = l.svcCtx.DB.MessageRecords.Query().Where(
-			messagerecords.BotWxid(wxinfo.Wxid),
-			messagerecords.SubSourceIDEQ(0),
-			messagerecords.SourceTypeIn(3, 4),
-			messagerecords.BotWxid(wxinfo.Wxid),
-			messagerecords.CreatedAtGTE(lastHour),
-			messagerecords.CreatedAtLT(currentHour),
-		).Count(l.ctx)
-
-		// 好友总数:contact 表中 type=1
-		friendCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
-			contact.Type(1),
-			contact.WxWxid(wxinfo.Wxid),
-		).Count(l.ctx)
-
-		// 群总数:contact 表中 type=2
-		groupCountInt, _ = l.svcCtx.DB.Contact.Query().Where(
-			contact.Type(2),
-			contact.WxWxid(wxinfo.Wxid),
-		).Count(l.ctx)
-
-		// 消耗Token数:usage_detail 表
-		consumeTokenInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
-			usagedetail.TypeEQ(1),
-			usagedetail.BotID(wxinfo.Wxid),
-			usagedetail.CreatedAtGTE(lastHour),
-			usagedetail.CreatedAtLT(currentHour),
-		).Aggregate(ent.Sum("total_tokens")).Int(l.ctx)
-
-		// 账户余额
-		accountBalanceInt = 0
-
-		// 活跃好友:usage_detail 表 type = 1
-		activeUserInt, _ = l.svcCtx.DB.UsageDetail.Query().Where(
-			usagedetail.Type(1),
-			usagedetail.BotID(wxinfo.Wxid),
-			usagedetail.CreatedAtGTE(lastHour),
-			usagedetail.CreatedAtLT(currentHour),
-		).GroupBy(usagedetail.FieldBotID).Int(l.ctx)
-
-		lastHourData, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
-			usagestatistichour.AddtimeEQ(uint64(lastHourInt)),
-			usagestatistichour.Type(1),
-			usagestatistichour.BotID(wxinfo.Wxid),
-		).First(l.ctx)
-
-		if lastHourData == nil {
-			newUserInt = friendCountInt
-		} else {
-			newUserInt = int(lastHourData.TotalFriend) - friendCountInt
-		}
+		var LabelsCount []custom_types.LabelDist
+		err := l.svcCtx.DB.LabelRelationship.Query().Where(labelrelationship.OrganizationIDEQ(orgID), labelrelationship.DeletedAtIsNil()).GroupBy(labelrelationship.FieldLabelID).Aggregate(ent.Count()).Scan(l.ctx, &LabelsCount)
+		l.Errorf("save hour data error:%v \n", err)
+		LabelsCountSet[orgID] = LabelsCount
 
-		_, err := l.svcCtx.DB.UsageStatisticHour.Create().
+		_, err = l.svcCtx.DB.UsageStatisticHour.Create().
 			SetType(1).
-			SetBotID(wxinfo.Wxid).
-			SetOrganizationID(wxinfo.OrganizationID).
-			SetAiResponse(uint64(aiResponseInt)).
-			SetSopRun(uint64(sopRunInt)).
-			SetTotalFriend(uint64(friendCountInt)).
-			SetTotalGroup(uint64(groupCountInt)).
-			SetAccountBalance(uint64(accountBalanceInt)).
-			SetConsumeToken(uint64(consumeTokenInt)).
-			SetActiveUser(uint64(activeUserInt)).
-			SetNewUser(int64(newUserInt)).
+			SetOrganizationID(orgID).
+			SetAiResponse(uint64(orgAiResponseInt)).
+			SetSopRun(uint64(orgSopRunInt)).
+			SetTotalFriend(uint64(orgFriendCountInt)).
+			SetTotalGroup(uint64(orgGroupCountInt)).
+			SetAccountBalance(uint64(orgAccountBalanceInt)).
+			SetConsumeToken(uint64(orgConsumeTokenInt)).
+			SetActiveUser(uint64(orgActiveUserInt)).
+			SetNewUser(int64(orgNewUserInt)).
 			SetAddtime(uint64(currentHourInt)).
+			SetNotNilLabelDist(LabelsCount).
 			Save(l.ctx)
 		l.Errorf("save hour data error:%v \n", err)
 	}
@@ -160,12 +210,75 @@ func (l *CronTask) computeStatistic() {
 	yesterdayLastHour := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
 	yesterdayLastHourInt, _ := strconv.Atoi(yesterdayLastHour.Format("20060102"))
 
-	for _, wxinfo := range wxbots {
-		l.Logger.Infof("开始计算日数据:%d\n", day)
-		// 先判断该账号是否已经统计了日数据,如果已经统计了,就不需要再统计了
+	for orgID, wxinfos := range wxbotsSet {
+		var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt uint64
+		var orgNewUserInt int64
+		for _, wxinfo := range wxinfos {
+			l.Logger.Infof("开始计算日数据:%d\n", day)
+			// 先判断该账号是否已经统计了日数据,如果已经统计了,就不需要再统计了
+			dayDataCount, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
+				usagestatisticday.Type(1),
+				usagestatisticday.BotID(wxinfo.Wxid),
+				usagestatisticday.Addtime(uint64(day)),
+			).Count(l.ctx)
+
+			// 如果添加过了就略过
+			if dayDataCount > 0 {
+				continue
+			}
+
+			hourDataBatch, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
+				usagestatistichour.Type(1),
+				usagestatistichour.BotID(wxinfo.Wxid),
+				usagestatistichour.AddtimeGTE(uint64(yesterdayFirstHourInt)),
+				usagestatistichour.AddtimeLT(uint64(yesterdayLastHourInt)),
+			).All(l.ctx)
+
+			var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
+			var newUser int64
+			for _, hourData := range hourDataBatch {
+				aiResponse += hourData.AiResponse
+				sopRun += hourData.SopRun
+				totalFriend += hourData.TotalFriend
+				totalGroup += hourData.TotalGroup
+				accountBalance += hourData.AccountBalance
+				consumeToken += hourData.ConsumeToken
+				activeUser += hourData.ActiveUser
+				newUser += hourData.NewUser
+			}
+			orgAiResponseInt += aiResponse
+			orgSopRunInt += sopRun
+			orgFriendCountInt += totalFriend
+			orgGroupCountInt += totalGroup
+			orgAccountBalanceInt += accountBalance
+			orgConsumeTokenInt += consumeToken
+			orgActiveUserInt += activeUser
+			orgNewUserInt += newUser
+
+			_, err := l.svcCtx.DB.UsageStatisticDay.Create().
+				SetAddtime(uint64(day)).
+				SetType(1).
+				SetBotID(wxinfo.Wxid).
+				SetOrganizationID(wxinfo.OrganizationID).
+				SetAiResponse(aiResponse).
+				SetSopRun(sopRun).
+				SetTotalFriend(totalFriend).
+				SetTotalGroup(totalGroup).
+				SetAccountBalance(accountBalance).
+				SetConsumeToken(consumeToken).
+				SetActiveUser(activeUser).
+				SetNewUser(newUser).
+				Save(l.ctx)
+			if err != nil {
+				l.Errorf("create day data error:%v \n", err)
+				continue
+			}
+		}
+		// 先判断该租户是否已经统计了日数据,如果已经统计了,就不需要再统计了
 		dayDataCount, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
 			usagestatisticday.Type(1),
-			usagestatisticday.BotID(wxinfo.Wxid),
+			usagestatisticday.OrganizationID(orgID),
+			usagestatisticday.BotIDIsNil(),
 			usagestatisticday.Addtime(uint64(day)),
 		).Count(l.ctx)
 
@@ -174,39 +287,19 @@ func (l *CronTask) computeStatistic() {
 			continue
 		}
 
-		hourDataBatch, _ := l.svcCtx.DB.UsageStatisticHour.Query().Where(
-			usagestatistichour.Type(1),
-			usagestatistichour.BotID(wxinfo.Wxid),
-			usagestatistichour.AddtimeGTE(uint64(yesterdayFirstHourInt)),
-			usagestatistichour.AddtimeLT(uint64(yesterdayLastHourInt)),
-		).All(l.ctx)
-
-		var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
-		var newUser int64
-		for _, hourData := range hourDataBatch {
-			aiResponse += hourData.AiResponse
-			sopRun += hourData.SopRun
-			totalFriend += hourData.TotalFriend
-			totalGroup += hourData.TotalGroup
-			accountBalance += hourData.AccountBalance
-			consumeToken += hourData.ConsumeToken
-			activeUser += hourData.ActiveUser
-			newUser += hourData.NewUser
-		}
-
 		_, err := l.svcCtx.DB.UsageStatisticDay.Create().
 			SetAddtime(uint64(day)).
 			SetType(1).
-			SetBotID(wxinfo.Wxid).
-			SetOrganizationID(wxinfo.OrganizationID).
-			SetAiResponse(aiResponse).
-			SetSopRun(sopRun).
-			SetTotalFriend(totalFriend).
-			SetTotalGroup(totalGroup).
-			SetAccountBalance(accountBalance).
-			SetConsumeToken(consumeToken).
-			SetActiveUser(activeUser).
-			SetNewUser(newUser).
+			SetOrganizationID(orgID).
+			SetAiResponse(orgAiResponseInt).
+			SetSopRun(orgSopRunInt).
+			SetTotalFriend(orgFriendCountInt).
+			SetTotalGroup(orgGroupCountInt).
+			SetAccountBalance(orgAccountBalanceInt).
+			SetConsumeToken(orgConsumeTokenInt).
+			SetActiveUser(orgActiveUserInt).
+			SetNewUser(orgNewUserInt).
+			SetNotNilLabelDist(LabelsCountSet[orgID]).
 			Save(l.ctx)
 		if err != nil {
 			l.Errorf("create day data error:%v \n", err)
@@ -223,21 +316,84 @@ func (l *CronTask) computeStatistic() {
 	monthStr := time.Now().Format("200601")
 	month, _ := strconv.Atoi(monthStr)
 
-	for _, wxinfo := range wxbots {
-		l.Logger.Infof("开始计算月数据:%d\n", month)
-
-		// 获取上月的第一天
-		monthFirstDay := time.Date(now.Year(), now.Month()-1, 1, 0, 0, 0, 0, now.Location())
-		monthFirstDayInt, _ := strconv.Atoi(monthFirstDay.Format("20060102"))
-
-		// 获取上月的最后一天
-		monthLastDay := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
-		monthLastDayInt, _ := strconv.Atoi(monthLastDay.Format("20060102"))
-
-		// 先判断该账号是否已经统计了月数据,如果已经统计了,就不需要再统计了
+	for orgID, wxinfos := range wxbotsSet {
+		var orgAiResponseInt, orgSopRunInt, orgFriendCountInt, orgGroupCountInt, orgAccountBalanceInt, orgConsumeTokenInt, orgActiveUserInt uint64
+		var orgNewUserInt int64
+		for _, wxinfo := range wxinfos {
+			l.Logger.Infof("开始计算月数据:%d\n", month)
+
+			// 获取上月的第一天
+			monthFirstDay := time.Date(now.Year(), now.Month()-1, 1, 0, 0, 0, 0, now.Location())
+			monthFirstDayInt, _ := strconv.Atoi(monthFirstDay.Format("20060102"))
+
+			// 获取上月的最后一天
+			monthLastDay := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
+			monthLastDayInt, _ := strconv.Atoi(monthLastDay.Format("20060102"))
+
+			// 先判断该账号是否已经统计了月数据,如果已经统计了,就不需要再统计了
+			monthDataCount, _ := l.svcCtx.DB.UsageStatisticMonth.Query().Where(
+				usagestatisticmonth.Type(1),
+				usagestatisticmonth.BotID(wxinfo.Wxid),
+				usagestatisticmonth.Addtime(uint64(month)),
+			).Count(l.ctx)
+
+			// 如果添加过了就略过
+			if monthDataCount > 0 {
+				continue
+			}
+
+			dayDataBatch, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
+				usagestatisticday.Type(1),
+				usagestatisticday.BotID(wxinfo.Wxid),
+				usagestatisticday.AddtimeGTE(uint64(monthFirstDayInt)),
+				usagestatisticday.AddtimeLT(uint64(monthLastDayInt)),
+			).All(l.ctx)
+
+			var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
+			var newUser int64
+			for _, dayData := range dayDataBatch {
+				aiResponse += dayData.AiResponse
+				sopRun += dayData.SopRun
+				totalFriend += dayData.TotalFriend
+				totalGroup += dayData.TotalGroup
+				accountBalance += dayData.AccountBalance
+				consumeToken += dayData.ConsumeToken
+				activeUser += dayData.ActiveUser
+				newUser += dayData.NewUser
+			}
+			orgAiResponseInt += aiResponse
+			orgSopRunInt += sopRun
+			orgFriendCountInt += totalFriend
+			orgGroupCountInt += totalGroup
+			orgAccountBalanceInt += accountBalance
+			orgConsumeTokenInt += consumeToken
+			orgActiveUserInt += activeUser
+			orgNewUserInt += newUser
+
+			_, err := l.svcCtx.DB.UsageStatisticMonth.Create().
+				SetAddtime(uint64(month)).
+				SetType(1).
+				SetBotID(wxinfo.Wxid).
+				SetOrganizationID(wxinfo.OrganizationID).
+				SetAiResponse(aiResponse).
+				SetSopRun(sopRun).
+				SetTotalFriend(totalFriend).
+				SetTotalGroup(totalGroup).
+				SetAccountBalance(accountBalance).
+				SetConsumeToken(consumeToken).
+				SetActiveUser(activeUser).
+				SetNewUser(newUser).
+				Save(l.ctx)
+			if err != nil {
+				l.Errorf("create month data error:%v \n", err)
+				continue
+			}
+		}
+		// 先判断该租户是否已经统计了月数据,如果已经统计了,就不需要再统计了
 		monthDataCount, _ := l.svcCtx.DB.UsageStatisticMonth.Query().Where(
 			usagestatisticmonth.Type(1),
-			usagestatisticmonth.BotID(wxinfo.Wxid),
+			usagestatisticmonth.OrganizationID(orgID),
+			usagestatisticmonth.BotIDIsNil(),
 			usagestatisticmonth.Addtime(uint64(month)),
 		).Count(l.ctx)
 
@@ -246,39 +402,19 @@ func (l *CronTask) computeStatistic() {
 			continue
 		}
 
-		dayDataBatch, _ := l.svcCtx.DB.UsageStatisticDay.Query().Where(
-			usagestatisticday.Type(1),
-			usagestatisticday.BotID(wxinfo.Wxid),
-			usagestatisticday.AddtimeGTE(uint64(monthFirstDayInt)),
-			usagestatisticday.AddtimeLT(uint64(monthLastDayInt)),
-		).All(l.ctx)
-
-		var aiResponse, sopRun, totalFriend, totalGroup, accountBalance, consumeToken, activeUser uint64
-		var newUser int64
-		for _, dayData := range dayDataBatch {
-			aiResponse += dayData.AiResponse
-			sopRun += dayData.SopRun
-			totalFriend += dayData.TotalFriend
-			totalGroup += dayData.TotalGroup
-			accountBalance += dayData.AccountBalance
-			consumeToken += dayData.ConsumeToken
-			activeUser += dayData.ActiveUser
-			newUser += dayData.NewUser
-		}
-
 		_, err := l.svcCtx.DB.UsageStatisticMonth.Create().
 			SetAddtime(uint64(month)).
 			SetType(1).
-			SetBotID(wxinfo.Wxid).
-			SetOrganizationID(wxinfo.OrganizationID).
-			SetAiResponse(aiResponse).
-			SetSopRun(sopRun).
-			SetTotalFriend(totalFriend).
-			SetTotalGroup(totalGroup).
-			SetAccountBalance(accountBalance).
-			SetConsumeToken(consumeToken).
-			SetActiveUser(activeUser).
-			SetNewUser(newUser).
+			SetOrganizationID(orgID).
+			SetAiResponse(orgAiResponseInt).
+			SetSopRun(orgSopRunInt).
+			SetTotalFriend(orgFriendCountInt).
+			SetTotalGroup(orgGroupCountInt).
+			SetAccountBalance(orgAccountBalanceInt).
+			SetConsumeToken(orgConsumeTokenInt).
+			SetActiveUser(orgActiveUserInt).
+			SetNewUser(orgNewUserInt).
+			SetNotNilLabelDist(LabelsCountSet[orgID]).
 			Save(l.ctx)
 		if err != nil {
 			l.Errorf("create month data error:%v \n", err)

+ 2 - 1
desc/wechat/dashboard.api

@@ -58,7 +58,7 @@ type (
     WxResp {
         BaseDataInfo
 
-        Data []WxList `json:"data"`
+        Data WxList `json:"data"`
     }
 
     WxList {
@@ -68,6 +68,7 @@ type (
     }
 
     WxData {
+        Nickname string `json:"nickname"`
         TotalFriend uint64 `json:"total_friend"`
         TotalGroup uint64 `json:"total_group"`
         InteractionRate float32 `json:"interaction_rate"`

+ 2 - 2
ent/custom_types/types.go

@@ -21,6 +21,6 @@ type ActionForward struct {
 }
 
 type LabelDist struct {
-	Value uint64 `json:"value"`
-	Name  string `json:"name"`
+	LabelID uint64 `json:"label_id"`
+	Count   uint64 `json:"count"`
 }

+ 6 - 3
ent/migrate/schema.go

@@ -752,7 +752,7 @@ var (
 		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
 		{Name: "addtime", Type: field.TypeUint64, Comment: "写入年月日"},
 		{Name: "type", Type: field.TypeInt, Comment: "1-微信 2-名片"},
-		{Name: "bot_id", Type: field.TypeString, Comment: "微信或名片id"},
+		{Name: "bot_id", Type: field.TypeString, Nullable: true, Comment: "微信或名片id"},
 		{Name: "organization_id", Type: field.TypeUint64, Nullable: true, Comment: "机构ID"},
 		{Name: "ai_response", Type: field.TypeUint64, Comment: "AI回复次数"},
 		{Name: "sop_run", Type: field.TypeUint64, Comment: "SOP运行次数"},
@@ -762,6 +762,7 @@ var (
 		{Name: "consume_token", Type: field.TypeUint64, Comment: "消耗token数"},
 		{Name: "active_user", Type: field.TypeUint64, Comment: "活跃用户数"},
 		{Name: "new_user", Type: field.TypeInt64, Comment: "新增用户数"},
+		{Name: "label_dist", Type: field.TypeJSON, Comment: "标签分布"},
 	}
 	// UsageStatisticDayTable holds the schema information for the "usage_statistic_day" table.
 	UsageStatisticDayTable = &schema.Table{
@@ -790,7 +791,7 @@ var (
 		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
 		{Name: "addtime", Type: field.TypeUint64, Comment: "写入小时"},
 		{Name: "type", Type: field.TypeInt, Comment: "1-微信 2-名片"},
-		{Name: "bot_id", Type: field.TypeString, Comment: "微信或名片id"},
+		{Name: "bot_id", Type: field.TypeString, Nullable: true, Comment: "微信或名片id"},
 		{Name: "organization_id", Type: field.TypeUint64, Nullable: true, Comment: "机构ID"},
 		{Name: "ai_response", Type: field.TypeUint64, Comment: "AI回复次数"},
 		{Name: "sop_run", Type: field.TypeUint64, Comment: "SOP运行次数"},
@@ -800,6 +801,7 @@ var (
 		{Name: "consume_token", Type: field.TypeUint64, Comment: "消耗token数"},
 		{Name: "active_user", Type: field.TypeUint64, Comment: "活跃用户数"},
 		{Name: "new_user", Type: field.TypeInt64, Comment: "新增用户数"},
+		{Name: "label_dist", Type: field.TypeJSON, Comment: "标签分布"},
 	}
 	// UsageStatisticHourTable holds the schema information for the "usage_statistic_hour" table.
 	UsageStatisticHourTable = &schema.Table{
@@ -828,7 +830,7 @@ var (
 		{Name: "deleted_at", Type: field.TypeTime, Nullable: true, Comment: "Delete Time | 删除日期"},
 		{Name: "addtime", Type: field.TypeUint64, Comment: "写入年月"},
 		{Name: "type", Type: field.TypeInt, Comment: "1-微信 2-名片"},
-		{Name: "bot_id", Type: field.TypeString, Comment: "微信或名片id"},
+		{Name: "bot_id", Type: field.TypeString, Nullable: true, Comment: "微信或名片id"},
 		{Name: "organization_id", Type: field.TypeUint64, Nullable: true, Comment: "机构ID"},
 		{Name: "ai_response", Type: field.TypeUint64, Comment: "AI回复次数"},
 		{Name: "sop_run", Type: field.TypeUint64, Comment: "SOP运行次数"},
@@ -838,6 +840,7 @@ var (
 		{Name: "consume_token", Type: field.TypeUint64, Comment: "消耗token数"},
 		{Name: "active_user", Type: field.TypeUint64, Comment: "活跃用户数"},
 		{Name: "new_user", Type: field.TypeInt64, Comment: "新增用户数"},
+		{Name: "label_dist", Type: field.TypeJSON, Comment: "标签分布"},
 	}
 	// UsageStatisticMonthTable holds the schema information for the "usage_statistic_month" table.
 	UsageStatisticMonthTable = &schema.Table{

+ 270 - 3
ent/mutation.go

@@ -25963,6 +25963,8 @@ type UsageStatisticDayMutation struct {
 	addactive_user     *int64
 	new_user           *int64
 	addnew_user        *int64
+	label_dist         *[]custom_types.LabelDist
+	appendlabel_dist   []custom_types.LabelDist
 	clearedFields      map[string]struct{}
 	done               bool
 	oldValue           func(context.Context) (*UsageStatisticDay, error)
@@ -26407,9 +26409,22 @@ func (m *UsageStatisticDayMutation) OldBotID(ctx context.Context) (v string, err
 	return oldValue.BotID, nil
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (m *UsageStatisticDayMutation) ClearBotID() {
+	m.bot_id = nil
+	m.clearedFields[usagestatisticday.FieldBotID] = struct{}{}
+}
+
+// BotIDCleared returns if the "bot_id" field was cleared in this mutation.
+func (m *UsageStatisticDayMutation) BotIDCleared() bool {
+	_, ok := m.clearedFields[usagestatisticday.FieldBotID]
+	return ok
+}
+
 // ResetBotID resets all changes to the "bot_id" field.
 func (m *UsageStatisticDayMutation) ResetBotID() {
 	m.bot_id = nil
+	delete(m.clearedFields, usagestatisticday.FieldBotID)
 }
 
 // SetOrganizationID sets the "organization_id" field.
@@ -26930,6 +26945,57 @@ func (m *UsageStatisticDayMutation) ResetNewUser() {
 	m.addnew_user = nil
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (m *UsageStatisticDayMutation) SetLabelDist(ctd []custom_types.LabelDist) {
+	m.label_dist = &ctd
+	m.appendlabel_dist = nil
+}
+
+// LabelDist returns the value of the "label_dist" field in the mutation.
+func (m *UsageStatisticDayMutation) LabelDist() (r []custom_types.LabelDist, exists bool) {
+	v := m.label_dist
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldLabelDist returns the old "label_dist" field's value of the UsageStatisticDay entity.
+// If the UsageStatisticDay object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UsageStatisticDayMutation) OldLabelDist(ctx context.Context) (v []custom_types.LabelDist, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldLabelDist is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldLabelDist requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldLabelDist: %w", err)
+	}
+	return oldValue.LabelDist, nil
+}
+
+// AppendLabelDist adds ctd to the "label_dist" field.
+func (m *UsageStatisticDayMutation) AppendLabelDist(ctd []custom_types.LabelDist) {
+	m.appendlabel_dist = append(m.appendlabel_dist, ctd...)
+}
+
+// AppendedLabelDist returns the list of values that were appended to the "label_dist" field in this mutation.
+func (m *UsageStatisticDayMutation) AppendedLabelDist() ([]custom_types.LabelDist, bool) {
+	if len(m.appendlabel_dist) == 0 {
+		return nil, false
+	}
+	return m.appendlabel_dist, true
+}
+
+// ResetLabelDist resets all changes to the "label_dist" field.
+func (m *UsageStatisticDayMutation) ResetLabelDist() {
+	m.label_dist = nil
+	m.appendlabel_dist = nil
+}
+
 // Where appends a list predicates to the UsageStatisticDayMutation builder.
 func (m *UsageStatisticDayMutation) Where(ps ...predicate.UsageStatisticDay) {
 	m.predicates = append(m.predicates, ps...)
@@ -26964,7 +27030,7 @@ func (m *UsageStatisticDayMutation) Type() string {
 // order to get all numeric fields that were incremented/decremented, call
 // AddedFields().
 func (m *UsageStatisticDayMutation) Fields() []string {
-	fields := make([]string, 0, 16)
+	fields := make([]string, 0, 17)
 	if m.created_at != nil {
 		fields = append(fields, usagestatisticday.FieldCreatedAt)
 	}
@@ -27013,6 +27079,9 @@ func (m *UsageStatisticDayMutation) Fields() []string {
 	if m.new_user != nil {
 		fields = append(fields, usagestatisticday.FieldNewUser)
 	}
+	if m.label_dist != nil {
+		fields = append(fields, usagestatisticday.FieldLabelDist)
+	}
 	return fields
 }
 
@@ -27053,6 +27122,8 @@ func (m *UsageStatisticDayMutation) Field(name string) (ent.Value, bool) {
 		return m.ActiveUser()
 	case usagestatisticday.FieldNewUser:
 		return m.NewUser()
+	case usagestatisticday.FieldLabelDist:
+		return m.LabelDist()
 	}
 	return nil, false
 }
@@ -27094,6 +27165,8 @@ func (m *UsageStatisticDayMutation) OldField(ctx context.Context, name string) (
 		return m.OldActiveUser(ctx)
 	case usagestatisticday.FieldNewUser:
 		return m.OldNewUser(ctx)
+	case usagestatisticday.FieldLabelDist:
+		return m.OldLabelDist(ctx)
 	}
 	return nil, fmt.Errorf("unknown UsageStatisticDay field %s", name)
 }
@@ -27215,6 +27288,13 @@ func (m *UsageStatisticDayMutation) SetField(name string, value ent.Value) error
 		}
 		m.SetNewUser(v)
 		return nil
+	case usagestatisticday.FieldLabelDist:
+		v, ok := value.([]custom_types.LabelDist)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetLabelDist(v)
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticDay field %s", name)
 }
@@ -27398,6 +27478,9 @@ func (m *UsageStatisticDayMutation) ClearedFields() []string {
 	if m.FieldCleared(usagestatisticday.FieldDeletedAt) {
 		fields = append(fields, usagestatisticday.FieldDeletedAt)
 	}
+	if m.FieldCleared(usagestatisticday.FieldBotID) {
+		fields = append(fields, usagestatisticday.FieldBotID)
+	}
 	if m.FieldCleared(usagestatisticday.FieldOrganizationID) {
 		fields = append(fields, usagestatisticday.FieldOrganizationID)
 	}
@@ -27421,6 +27504,9 @@ func (m *UsageStatisticDayMutation) ClearField(name string) error {
 	case usagestatisticday.FieldDeletedAt:
 		m.ClearDeletedAt()
 		return nil
+	case usagestatisticday.FieldBotID:
+		m.ClearBotID()
+		return nil
 	case usagestatisticday.FieldOrganizationID:
 		m.ClearOrganizationID()
 		return nil
@@ -27480,6 +27566,9 @@ func (m *UsageStatisticDayMutation) ResetField(name string) error {
 	case usagestatisticday.FieldNewUser:
 		m.ResetNewUser()
 		return nil
+	case usagestatisticday.FieldLabelDist:
+		m.ResetLabelDist()
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticDay field %s", name)
 }
@@ -27566,6 +27655,8 @@ type UsageStatisticHourMutation struct {
 	addactive_user     *int64
 	new_user           *int64
 	addnew_user        *int64
+	label_dist         *[]custom_types.LabelDist
+	appendlabel_dist   []custom_types.LabelDist
 	clearedFields      map[string]struct{}
 	done               bool
 	oldValue           func(context.Context) (*UsageStatisticHour, error)
@@ -28010,9 +28101,22 @@ func (m *UsageStatisticHourMutation) OldBotID(ctx context.Context) (v string, er
 	return oldValue.BotID, nil
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (m *UsageStatisticHourMutation) ClearBotID() {
+	m.bot_id = nil
+	m.clearedFields[usagestatistichour.FieldBotID] = struct{}{}
+}
+
+// BotIDCleared returns if the "bot_id" field was cleared in this mutation.
+func (m *UsageStatisticHourMutation) BotIDCleared() bool {
+	_, ok := m.clearedFields[usagestatistichour.FieldBotID]
+	return ok
+}
+
 // ResetBotID resets all changes to the "bot_id" field.
 func (m *UsageStatisticHourMutation) ResetBotID() {
 	m.bot_id = nil
+	delete(m.clearedFields, usagestatistichour.FieldBotID)
 }
 
 // SetOrganizationID sets the "organization_id" field.
@@ -28533,6 +28637,57 @@ func (m *UsageStatisticHourMutation) ResetNewUser() {
 	m.addnew_user = nil
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (m *UsageStatisticHourMutation) SetLabelDist(ctd []custom_types.LabelDist) {
+	m.label_dist = &ctd
+	m.appendlabel_dist = nil
+}
+
+// LabelDist returns the value of the "label_dist" field in the mutation.
+func (m *UsageStatisticHourMutation) LabelDist() (r []custom_types.LabelDist, exists bool) {
+	v := m.label_dist
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldLabelDist returns the old "label_dist" field's value of the UsageStatisticHour entity.
+// If the UsageStatisticHour object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UsageStatisticHourMutation) OldLabelDist(ctx context.Context) (v []custom_types.LabelDist, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldLabelDist is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldLabelDist requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldLabelDist: %w", err)
+	}
+	return oldValue.LabelDist, nil
+}
+
+// AppendLabelDist adds ctd to the "label_dist" field.
+func (m *UsageStatisticHourMutation) AppendLabelDist(ctd []custom_types.LabelDist) {
+	m.appendlabel_dist = append(m.appendlabel_dist, ctd...)
+}
+
+// AppendedLabelDist returns the list of values that were appended to the "label_dist" field in this mutation.
+func (m *UsageStatisticHourMutation) AppendedLabelDist() ([]custom_types.LabelDist, bool) {
+	if len(m.appendlabel_dist) == 0 {
+		return nil, false
+	}
+	return m.appendlabel_dist, true
+}
+
+// ResetLabelDist resets all changes to the "label_dist" field.
+func (m *UsageStatisticHourMutation) ResetLabelDist() {
+	m.label_dist = nil
+	m.appendlabel_dist = nil
+}
+
 // Where appends a list predicates to the UsageStatisticHourMutation builder.
 func (m *UsageStatisticHourMutation) Where(ps ...predicate.UsageStatisticHour) {
 	m.predicates = append(m.predicates, ps...)
@@ -28567,7 +28722,7 @@ func (m *UsageStatisticHourMutation) Type() string {
 // order to get all numeric fields that were incremented/decremented, call
 // AddedFields().
 func (m *UsageStatisticHourMutation) Fields() []string {
-	fields := make([]string, 0, 16)
+	fields := make([]string, 0, 17)
 	if m.created_at != nil {
 		fields = append(fields, usagestatistichour.FieldCreatedAt)
 	}
@@ -28616,6 +28771,9 @@ func (m *UsageStatisticHourMutation) Fields() []string {
 	if m.new_user != nil {
 		fields = append(fields, usagestatistichour.FieldNewUser)
 	}
+	if m.label_dist != nil {
+		fields = append(fields, usagestatistichour.FieldLabelDist)
+	}
 	return fields
 }
 
@@ -28656,6 +28814,8 @@ func (m *UsageStatisticHourMutation) Field(name string) (ent.Value, bool) {
 		return m.ActiveUser()
 	case usagestatistichour.FieldNewUser:
 		return m.NewUser()
+	case usagestatistichour.FieldLabelDist:
+		return m.LabelDist()
 	}
 	return nil, false
 }
@@ -28697,6 +28857,8 @@ func (m *UsageStatisticHourMutation) OldField(ctx context.Context, name string)
 		return m.OldActiveUser(ctx)
 	case usagestatistichour.FieldNewUser:
 		return m.OldNewUser(ctx)
+	case usagestatistichour.FieldLabelDist:
+		return m.OldLabelDist(ctx)
 	}
 	return nil, fmt.Errorf("unknown UsageStatisticHour field %s", name)
 }
@@ -28818,6 +28980,13 @@ func (m *UsageStatisticHourMutation) SetField(name string, value ent.Value) erro
 		}
 		m.SetNewUser(v)
 		return nil
+	case usagestatistichour.FieldLabelDist:
+		v, ok := value.([]custom_types.LabelDist)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetLabelDist(v)
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticHour field %s", name)
 }
@@ -29001,6 +29170,9 @@ func (m *UsageStatisticHourMutation) ClearedFields() []string {
 	if m.FieldCleared(usagestatistichour.FieldDeletedAt) {
 		fields = append(fields, usagestatistichour.FieldDeletedAt)
 	}
+	if m.FieldCleared(usagestatistichour.FieldBotID) {
+		fields = append(fields, usagestatistichour.FieldBotID)
+	}
 	if m.FieldCleared(usagestatistichour.FieldOrganizationID) {
 		fields = append(fields, usagestatistichour.FieldOrganizationID)
 	}
@@ -29024,6 +29196,9 @@ func (m *UsageStatisticHourMutation) ClearField(name string) error {
 	case usagestatistichour.FieldDeletedAt:
 		m.ClearDeletedAt()
 		return nil
+	case usagestatistichour.FieldBotID:
+		m.ClearBotID()
+		return nil
 	case usagestatistichour.FieldOrganizationID:
 		m.ClearOrganizationID()
 		return nil
@@ -29083,6 +29258,9 @@ func (m *UsageStatisticHourMutation) ResetField(name string) error {
 	case usagestatistichour.FieldNewUser:
 		m.ResetNewUser()
 		return nil
+	case usagestatistichour.FieldLabelDist:
+		m.ResetLabelDist()
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticHour field %s", name)
 }
@@ -29169,6 +29347,8 @@ type UsageStatisticMonthMutation struct {
 	addactive_user     *int64
 	new_user           *int64
 	addnew_user        *int64
+	label_dist         *[]custom_types.LabelDist
+	appendlabel_dist   []custom_types.LabelDist
 	clearedFields      map[string]struct{}
 	done               bool
 	oldValue           func(context.Context) (*UsageStatisticMonth, error)
@@ -29613,9 +29793,22 @@ func (m *UsageStatisticMonthMutation) OldBotID(ctx context.Context) (v string, e
 	return oldValue.BotID, nil
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (m *UsageStatisticMonthMutation) ClearBotID() {
+	m.bot_id = nil
+	m.clearedFields[usagestatisticmonth.FieldBotID] = struct{}{}
+}
+
+// BotIDCleared returns if the "bot_id" field was cleared in this mutation.
+func (m *UsageStatisticMonthMutation) BotIDCleared() bool {
+	_, ok := m.clearedFields[usagestatisticmonth.FieldBotID]
+	return ok
+}
+
 // ResetBotID resets all changes to the "bot_id" field.
 func (m *UsageStatisticMonthMutation) ResetBotID() {
 	m.bot_id = nil
+	delete(m.clearedFields, usagestatisticmonth.FieldBotID)
 }
 
 // SetOrganizationID sets the "organization_id" field.
@@ -30136,6 +30329,57 @@ func (m *UsageStatisticMonthMutation) ResetNewUser() {
 	m.addnew_user = nil
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (m *UsageStatisticMonthMutation) SetLabelDist(ctd []custom_types.LabelDist) {
+	m.label_dist = &ctd
+	m.appendlabel_dist = nil
+}
+
+// LabelDist returns the value of the "label_dist" field in the mutation.
+func (m *UsageStatisticMonthMutation) LabelDist() (r []custom_types.LabelDist, exists bool) {
+	v := m.label_dist
+	if v == nil {
+		return
+	}
+	return *v, true
+}
+
+// OldLabelDist returns the old "label_dist" field's value of the UsageStatisticMonth entity.
+// If the UsageStatisticMonth object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UsageStatisticMonthMutation) OldLabelDist(ctx context.Context) (v []custom_types.LabelDist, err error) {
+	if !m.op.Is(OpUpdateOne) {
+		return v, errors.New("OldLabelDist is only allowed on UpdateOne operations")
+	}
+	if m.id == nil || m.oldValue == nil {
+		return v, errors.New("OldLabelDist requires an ID field in the mutation")
+	}
+	oldValue, err := m.oldValue(ctx)
+	if err != nil {
+		return v, fmt.Errorf("querying old value for OldLabelDist: %w", err)
+	}
+	return oldValue.LabelDist, nil
+}
+
+// AppendLabelDist adds ctd to the "label_dist" field.
+func (m *UsageStatisticMonthMutation) AppendLabelDist(ctd []custom_types.LabelDist) {
+	m.appendlabel_dist = append(m.appendlabel_dist, ctd...)
+}
+
+// AppendedLabelDist returns the list of values that were appended to the "label_dist" field in this mutation.
+func (m *UsageStatisticMonthMutation) AppendedLabelDist() ([]custom_types.LabelDist, bool) {
+	if len(m.appendlabel_dist) == 0 {
+		return nil, false
+	}
+	return m.appendlabel_dist, true
+}
+
+// ResetLabelDist resets all changes to the "label_dist" field.
+func (m *UsageStatisticMonthMutation) ResetLabelDist() {
+	m.label_dist = nil
+	m.appendlabel_dist = nil
+}
+
 // Where appends a list predicates to the UsageStatisticMonthMutation builder.
 func (m *UsageStatisticMonthMutation) Where(ps ...predicate.UsageStatisticMonth) {
 	m.predicates = append(m.predicates, ps...)
@@ -30170,7 +30414,7 @@ func (m *UsageStatisticMonthMutation) Type() string {
 // order to get all numeric fields that were incremented/decremented, call
 // AddedFields().
 func (m *UsageStatisticMonthMutation) Fields() []string {
-	fields := make([]string, 0, 16)
+	fields := make([]string, 0, 17)
 	if m.created_at != nil {
 		fields = append(fields, usagestatisticmonth.FieldCreatedAt)
 	}
@@ -30219,6 +30463,9 @@ func (m *UsageStatisticMonthMutation) Fields() []string {
 	if m.new_user != nil {
 		fields = append(fields, usagestatisticmonth.FieldNewUser)
 	}
+	if m.label_dist != nil {
+		fields = append(fields, usagestatisticmonth.FieldLabelDist)
+	}
 	return fields
 }
 
@@ -30259,6 +30506,8 @@ func (m *UsageStatisticMonthMutation) Field(name string) (ent.Value, bool) {
 		return m.ActiveUser()
 	case usagestatisticmonth.FieldNewUser:
 		return m.NewUser()
+	case usagestatisticmonth.FieldLabelDist:
+		return m.LabelDist()
 	}
 	return nil, false
 }
@@ -30300,6 +30549,8 @@ func (m *UsageStatisticMonthMutation) OldField(ctx context.Context, name string)
 		return m.OldActiveUser(ctx)
 	case usagestatisticmonth.FieldNewUser:
 		return m.OldNewUser(ctx)
+	case usagestatisticmonth.FieldLabelDist:
+		return m.OldLabelDist(ctx)
 	}
 	return nil, fmt.Errorf("unknown UsageStatisticMonth field %s", name)
 }
@@ -30421,6 +30672,13 @@ func (m *UsageStatisticMonthMutation) SetField(name string, value ent.Value) err
 		}
 		m.SetNewUser(v)
 		return nil
+	case usagestatisticmonth.FieldLabelDist:
+		v, ok := value.([]custom_types.LabelDist)
+		if !ok {
+			return fmt.Errorf("unexpected type %T for field %s", value, name)
+		}
+		m.SetLabelDist(v)
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticMonth field %s", name)
 }
@@ -30604,6 +30862,9 @@ func (m *UsageStatisticMonthMutation) ClearedFields() []string {
 	if m.FieldCleared(usagestatisticmonth.FieldDeletedAt) {
 		fields = append(fields, usagestatisticmonth.FieldDeletedAt)
 	}
+	if m.FieldCleared(usagestatisticmonth.FieldBotID) {
+		fields = append(fields, usagestatisticmonth.FieldBotID)
+	}
 	if m.FieldCleared(usagestatisticmonth.FieldOrganizationID) {
 		fields = append(fields, usagestatisticmonth.FieldOrganizationID)
 	}
@@ -30627,6 +30888,9 @@ func (m *UsageStatisticMonthMutation) ClearField(name string) error {
 	case usagestatisticmonth.FieldDeletedAt:
 		m.ClearDeletedAt()
 		return nil
+	case usagestatisticmonth.FieldBotID:
+		m.ClearBotID()
+		return nil
 	case usagestatisticmonth.FieldOrganizationID:
 		m.ClearOrganizationID()
 		return nil
@@ -30686,6 +30950,9 @@ func (m *UsageStatisticMonthMutation) ResetField(name string) error {
 	case usagestatisticmonth.FieldNewUser:
 		m.ResetNewUser()
 		return nil
+	case usagestatisticmonth.FieldLabelDist:
+		m.ResetLabelDist()
+		return nil
 	}
 	return fmt.Errorf("unknown UsageStatisticMonth field %s", name)
 }

+ 5 - 1
ent/schema/usage_statistic_day.go

@@ -1,6 +1,7 @@
 package schema
 
 import (
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/schema/localmixin"
 
 	"entgo.io/ent"
@@ -19,7 +20,7 @@ func (UsageStatisticDay) Fields() []ent.Field {
 	return []ent.Field{
 		field.Uint64("addtime").Comment("写入年月日"),
 		field.Int("type").Comment("1-微信 2-名片"),
-		field.String("bot_id").Comment("微信或名片id"),
+		field.String("bot_id").Optional().Comment("微信或名片id"),
 		field.Uint64("organization_id").Optional().Comment("机构ID"),
 		field.Uint64("ai_response").Comment("AI回复次数"),
 		field.Uint64("sop_run").Comment("SOP运行次数"),
@@ -29,6 +30,9 @@ func (UsageStatisticDay) Fields() []ent.Field {
 		field.Uint64("consume_token").Comment("消耗token数"),
 		field.Uint64("active_user").Comment("活跃用户数"),
 		field.Int64("new_user").Comment("新增用户数"),
+		field.JSON("label_dist", []custom_types.LabelDist{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("标签分布"),
 	}
 }
 

+ 1 - 1
ent/schema/usage_statistic_hour.go

@@ -20,7 +20,7 @@ func (UsageStatisticHour) Fields() []ent.Field {
 	return []ent.Field{
 		field.Uint64("addtime").Comment("写入小时"),
 		field.Int("type").Comment("1-微信 2-名片"),
-		field.String("bot_id").Comment("微信或名片id"),
+		field.String("bot_id").Optional().Comment("微信或名片id"),
 		field.Uint64("organization_id").Optional().Comment("机构ID"),
 		field.Uint64("ai_response").Comment("AI回复次数"),
 		field.Uint64("sop_run").Comment("SOP运行次数"),

+ 5 - 1
ent/schema/usage_statistic_month.go

@@ -1,6 +1,7 @@
 package schema
 
 import (
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/schema/localmixin"
 
 	"entgo.io/ent"
@@ -19,7 +20,7 @@ func (UsageStatisticMonth) Fields() []ent.Field {
 	return []ent.Field{
 		field.Uint64("addtime").Comment("写入年月"),
 		field.Int("type").Comment("1-微信 2-名片"),
-		field.String("bot_id").Comment("微信或名片id"),
+		field.String("bot_id").Optional().Comment("微信或名片id"),
 		field.Uint64("organization_id").Optional().Comment("机构ID"),
 		field.Uint64("ai_response").Comment("AI回复次数"),
 		field.Uint64("sop_run").Comment("SOP运行次数"),
@@ -29,6 +30,9 @@ func (UsageStatisticMonth) Fields() []ent.Field {
 		field.Uint64("consume_token").Comment("消耗token数"),
 		field.Uint64("active_user").Comment("活跃用户数"),
 		field.Int64("new_user").Comment("新增用户数"),
+		field.JSON("label_dist", []custom_types.LabelDist{}).
+			Annotations(entsql.WithComments(true)).
+			Comment("标签分布"),
 	}
 }
 

+ 72 - 0
ent/set_not_nil.go

@@ -5840,6 +5840,30 @@ func (usd *UsageStatisticDayCreate) SetNotNilNewUser(value *int64) *UsageStatist
 }
 
 // set field if value's pointer is not nil.
+func (usd *UsageStatisticDayUpdate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticDayUpdate {
+	if value != nil {
+		return usd.SetLabelDist(value)
+	}
+	return usd
+}
+
+// set field if value's pointer is not nil.
+func (usd *UsageStatisticDayUpdateOne) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticDayUpdateOne {
+	if value != nil {
+		return usd.SetLabelDist(value)
+	}
+	return usd
+}
+
+// set field if value's pointer is not nil.
+func (usd *UsageStatisticDayCreate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticDayCreate {
+	if value != nil {
+		return usd.SetLabelDist(value)
+	}
+	return usd
+}
+
+// set field if value's pointer is not nil.
 func (ush *UsageStatisticHourUpdate) SetNotNilUpdatedAt(value *time.Time) *UsageStatisticHourUpdate {
 	if value != nil {
 		return ush.SetUpdatedAt(*value)
@@ -6200,6 +6224,30 @@ func (ush *UsageStatisticHourCreate) SetNotNilNewUser(value *int64) *UsageStatis
 }
 
 // set field if value's pointer is not nil.
+func (ush *UsageStatisticHourUpdate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticHourUpdate {
+	if value != nil {
+		return ush.SetLabelDist(value)
+	}
+	return ush
+}
+
+// set field if value's pointer is not nil.
+func (ush *UsageStatisticHourUpdateOne) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticHourUpdateOne {
+	if value != nil {
+		return ush.SetLabelDist(value)
+	}
+	return ush
+}
+
+// set field if value's pointer is not nil.
+func (ush *UsageStatisticHourCreate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticHourCreate {
+	if value != nil {
+		return ush.SetLabelDist(value)
+	}
+	return ush
+}
+
+// set field if value's pointer is not nil.
 func (usm *UsageStatisticMonthUpdate) SetNotNilUpdatedAt(value *time.Time) *UsageStatisticMonthUpdate {
 	if value != nil {
 		return usm.SetUpdatedAt(*value)
@@ -6560,6 +6608,30 @@ func (usm *UsageStatisticMonthCreate) SetNotNilNewUser(value *int64) *UsageStati
 }
 
 // set field if value's pointer is not nil.
+func (usm *UsageStatisticMonthUpdate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticMonthUpdate {
+	if value != nil {
+		return usm.SetLabelDist(value)
+	}
+	return usm
+}
+
+// set field if value's pointer is not nil.
+func (usm *UsageStatisticMonthUpdateOne) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticMonthUpdateOne {
+	if value != nil {
+		return usm.SetLabelDist(value)
+	}
+	return usm
+}
+
+// set field if value's pointer is not nil.
+func (usm *UsageStatisticMonthCreate) SetNotNilLabelDist(value []custom_types.LabelDist) *UsageStatisticMonthCreate {
+	if value != nil {
+		return usm.SetLabelDist(value)
+	}
+	return usm
+}
+
+// set field if value's pointer is not nil.
 func (ut *UsageTotalUpdate) SetNotNilUpdatedAt(value *time.Time) *UsageTotalUpdate {
 	if value != nil {
 		return ut.SetUpdatedAt(*value)

+ 18 - 1
ent/usagestatisticday.go

@@ -3,9 +3,11 @@
 package ent
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatisticday"
 
 	"entgo.io/ent"
@@ -48,7 +50,9 @@ type UsageStatisticDay struct {
 	// 活跃用户数
 	ActiveUser uint64 `json:"active_user,omitempty"`
 	// 新增用户数
-	NewUser      int64 `json:"new_user,omitempty"`
+	NewUser int64 `json:"new_user,omitempty"`
+	// 标签分布
+	LabelDist    []custom_types.LabelDist `json:"label_dist,omitempty"`
 	selectValues sql.SelectValues
 }
 
@@ -57,6 +61,8 @@ func (*UsageStatisticDay) scanValues(columns []string) ([]any, error) {
 	values := make([]any, len(columns))
 	for i := range columns {
 		switch columns[i] {
+		case usagestatisticday.FieldLabelDist:
+			values[i] = new([]byte)
 		case usagestatisticday.FieldID, usagestatisticday.FieldStatus, usagestatisticday.FieldAddtime, usagestatisticday.FieldType, usagestatisticday.FieldOrganizationID, usagestatisticday.FieldAiResponse, usagestatisticday.FieldSopRun, usagestatisticday.FieldTotalFriend, usagestatisticday.FieldTotalGroup, usagestatisticday.FieldAccountBalance, usagestatisticday.FieldConsumeToken, usagestatisticday.FieldActiveUser, usagestatisticday.FieldNewUser:
 			values[i] = new(sql.NullInt64)
 		case usagestatisticday.FieldBotID:
@@ -180,6 +186,14 @@ func (usd *UsageStatisticDay) assignValues(columns []string, values []any) error
 			} else if value.Valid {
 				usd.NewUser = value.Int64
 			}
+		case usagestatisticday.FieldLabelDist:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field label_dist", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &usd.LabelDist); err != nil {
+					return fmt.Errorf("unmarshal field label_dist: %w", err)
+				}
+			}
 		default:
 			usd.selectValues.Set(columns[i], values[i])
 		}
@@ -263,6 +277,9 @@ func (usd *UsageStatisticDay) String() string {
 	builder.WriteString(", ")
 	builder.WriteString("new_user=")
 	builder.WriteString(fmt.Sprintf("%v", usd.NewUser))
+	builder.WriteString(", ")
+	builder.WriteString("label_dist=")
+	builder.WriteString(fmt.Sprintf("%v", usd.LabelDist))
 	builder.WriteByte(')')
 	return builder.String()
 }

+ 3 - 0
ent/usagestatisticday/usagestatisticday.go

@@ -46,6 +46,8 @@ const (
 	FieldActiveUser = "active_user"
 	// FieldNewUser holds the string denoting the new_user field in the database.
 	FieldNewUser = "new_user"
+	// FieldLabelDist holds the string denoting the label_dist field in the database.
+	FieldLabelDist = "label_dist"
 	// Table holds the table name of the usagestatisticday in the database.
 	Table = "usage_statistic_day"
 )
@@ -69,6 +71,7 @@ var Columns = []string{
 	FieldConsumeToken,
 	FieldActiveUser,
 	FieldNewUser,
+	FieldLabelDist,
 }
 
 // ValidColumn reports if the column name is valid (part of the table columns).

+ 10 - 0
ent/usagestatisticday/where.go

@@ -449,6 +449,16 @@ func BotIDHasSuffix(v string) predicate.UsageStatisticDay {
 	return predicate.UsageStatisticDay(sql.FieldHasSuffix(FieldBotID, v))
 }
 
+// BotIDIsNil applies the IsNil predicate on the "bot_id" field.
+func BotIDIsNil() predicate.UsageStatisticDay {
+	return predicate.UsageStatisticDay(sql.FieldIsNull(FieldBotID))
+}
+
+// BotIDNotNil applies the NotNil predicate on the "bot_id" field.
+func BotIDNotNil() predicate.UsageStatisticDay {
+	return predicate.UsageStatisticDay(sql.FieldNotNull(FieldBotID))
+}
+
 // BotIDEqualFold applies the EqualFold predicate on the "bot_id" field.
 func BotIDEqualFold(v string) predicate.UsageStatisticDay {
 	return predicate.UsageStatisticDay(sql.FieldEqualFold(FieldBotID, v))

+ 82 - 3
ent/usagestatisticday_create.go

@@ -7,6 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatisticday"
 
 	"entgo.io/ent/dialect/sql"
@@ -96,6 +97,14 @@ func (usdc *UsageStatisticDayCreate) SetBotID(s string) *UsageStatisticDayCreate
 	return usdc
 }
 
+// SetNillableBotID sets the "bot_id" field if the given value is not nil.
+func (usdc *UsageStatisticDayCreate) SetNillableBotID(s *string) *UsageStatisticDayCreate {
+	if s != nil {
+		usdc.SetBotID(*s)
+	}
+	return usdc
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usdc *UsageStatisticDayCreate) SetOrganizationID(u uint64) *UsageStatisticDayCreate {
 	usdc.mutation.SetOrganizationID(u)
@@ -158,6 +167,12 @@ func (usdc *UsageStatisticDayCreate) SetNewUser(i int64) *UsageStatisticDayCreat
 	return usdc
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usdc *UsageStatisticDayCreate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticDayCreate {
+	usdc.mutation.SetLabelDist(ctd)
+	return usdc
+}
+
 // SetID sets the "id" field.
 func (usdc *UsageStatisticDayCreate) SetID(u uint64) *UsageStatisticDayCreate {
 	usdc.mutation.SetID(u)
@@ -236,9 +251,6 @@ func (usdc *UsageStatisticDayCreate) check() error {
 	if _, ok := usdc.mutation.GetType(); !ok {
 		return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "UsageStatisticDay.type"`)}
 	}
-	if _, ok := usdc.mutation.BotID(); !ok {
-		return &ValidationError{Name: "bot_id", err: errors.New(`ent: missing required field "UsageStatisticDay.bot_id"`)}
-	}
 	if _, ok := usdc.mutation.AiResponse(); !ok {
 		return &ValidationError{Name: "ai_response", err: errors.New(`ent: missing required field "UsageStatisticDay.ai_response"`)}
 	}
@@ -263,6 +275,9 @@ func (usdc *UsageStatisticDayCreate) check() error {
 	if _, ok := usdc.mutation.NewUser(); !ok {
 		return &ValidationError{Name: "new_user", err: errors.New(`ent: missing required field "UsageStatisticDay.new_user"`)}
 	}
+	if _, ok := usdc.mutation.LabelDist(); !ok {
+		return &ValidationError{Name: "label_dist", err: errors.New(`ent: missing required field "UsageStatisticDay.label_dist"`)}
+	}
 	return nil
 }
 
@@ -360,6 +375,10 @@ func (usdc *UsageStatisticDayCreate) createSpec() (*UsageStatisticDay, *sqlgraph
 		_spec.SetField(usagestatisticday.FieldNewUser, field.TypeInt64, value)
 		_node.NewUser = value
 	}
+	if value, ok := usdc.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticday.FieldLabelDist, field.TypeJSON, value)
+		_node.LabelDist = value
+	}
 	return _node, _spec
 }
 
@@ -514,6 +533,12 @@ func (u *UsageStatisticDayUpsert) UpdateBotID() *UsageStatisticDayUpsert {
 	return u
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticDayUpsert) ClearBotID() *UsageStatisticDayUpsert {
+	u.SetNull(usagestatisticday.FieldBotID)
+	return u
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticDayUpsert) SetOrganizationID(v uint64) *UsageStatisticDayUpsert {
 	u.Set(usagestatisticday.FieldOrganizationID, v)
@@ -682,6 +707,18 @@ func (u *UsageStatisticDayUpsert) AddNewUser(v int64) *UsageStatisticDayUpsert {
 	return u
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticDayUpsert) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticDayUpsert {
+	u.Set(usagestatisticday.FieldLabelDist, v)
+	return u
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticDayUpsert) UpdateLabelDist() *UsageStatisticDayUpsert {
+	u.SetExcluded(usagestatisticday.FieldLabelDist)
+	return u
+}
+
 // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
 // Using this option is equivalent to using:
 //
@@ -852,6 +889,13 @@ func (u *UsageStatisticDayUpsertOne) UpdateBotID() *UsageStatisticDayUpsertOne {
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticDayUpsertOne) ClearBotID() *UsageStatisticDayUpsertOne {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticDayUpsertOne) SetOrganizationID(v uint64) *UsageStatisticDayUpsertOne {
 	return u.Update(func(s *UsageStatisticDayUpsert) {
@@ -1048,6 +1092,20 @@ func (u *UsageStatisticDayUpsertOne) UpdateNewUser() *UsageStatisticDayUpsertOne
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticDayUpsertOne) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticDayUpsertOne {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticDayUpsertOne) UpdateLabelDist() *UsageStatisticDayUpsertOne {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticDayUpsertOne) Exec(ctx context.Context) error {
 	if len(u.create.conflict) == 0 {
@@ -1384,6 +1442,13 @@ func (u *UsageStatisticDayUpsertBulk) UpdateBotID() *UsageStatisticDayUpsertBulk
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticDayUpsertBulk) ClearBotID() *UsageStatisticDayUpsertBulk {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticDayUpsertBulk) SetOrganizationID(v uint64) *UsageStatisticDayUpsertBulk {
 	return u.Update(func(s *UsageStatisticDayUpsert) {
@@ -1580,6 +1645,20 @@ func (u *UsageStatisticDayUpsertBulk) UpdateNewUser() *UsageStatisticDayUpsertBu
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticDayUpsertBulk) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticDayUpsertBulk {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticDayUpsertBulk) UpdateLabelDist() *UsageStatisticDayUpsertBulk {
+	return u.Update(func(s *UsageStatisticDayUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticDayUpsertBulk) Exec(ctx context.Context) error {
 	if u.create.err != nil {

+ 60 - 0
ent/usagestatisticday_update.go

@@ -7,11 +7,13 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/usagestatisticday"
 
 	"entgo.io/ent/dialect/sql"
 	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
 	"entgo.io/ent/schema/field"
 )
 
@@ -137,6 +139,12 @@ func (usdu *UsageStatisticDayUpdate) SetNillableBotID(s *string) *UsageStatistic
 	return usdu
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (usdu *UsageStatisticDayUpdate) ClearBotID() *UsageStatisticDayUpdate {
+	usdu.mutation.ClearBotID()
+	return usdu
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usdu *UsageStatisticDayUpdate) SetOrganizationID(u uint64) *UsageStatisticDayUpdate {
 	usdu.mutation.ResetOrganizationID()
@@ -332,6 +340,18 @@ func (usdu *UsageStatisticDayUpdate) AddNewUser(i int64) *UsageStatisticDayUpdat
 	return usdu
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usdu *UsageStatisticDayUpdate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticDayUpdate {
+	usdu.mutation.SetLabelDist(ctd)
+	return usdu
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (usdu *UsageStatisticDayUpdate) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticDayUpdate {
+	usdu.mutation.AppendLabelDist(ctd)
+	return usdu
+}
+
 // Mutation returns the UsageStatisticDayMutation object of the builder.
 func (usdu *UsageStatisticDayUpdate) Mutation() *UsageStatisticDayMutation {
 	return usdu.mutation
@@ -421,6 +441,9 @@ func (usdu *UsageStatisticDayUpdate) sqlSave(ctx context.Context) (n int, err er
 	if value, ok := usdu.mutation.BotID(); ok {
 		_spec.SetField(usagestatisticday.FieldBotID, field.TypeString, value)
 	}
+	if usdu.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatisticday.FieldBotID, field.TypeString)
+	}
 	if value, ok := usdu.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatisticday.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -478,6 +501,14 @@ func (usdu *UsageStatisticDayUpdate) sqlSave(ctx context.Context) (n int, err er
 	if value, ok := usdu.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatisticday.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := usdu.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticday.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := usdu.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatisticday.FieldLabelDist, value)
+		})
+	}
 	if n, err = sqlgraph.UpdateNodes(ctx, usdu.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{usagestatisticday.Label}
@@ -607,6 +638,12 @@ func (usduo *UsageStatisticDayUpdateOne) SetNillableBotID(s *string) *UsageStati
 	return usduo
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (usduo *UsageStatisticDayUpdateOne) ClearBotID() *UsageStatisticDayUpdateOne {
+	usduo.mutation.ClearBotID()
+	return usduo
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usduo *UsageStatisticDayUpdateOne) SetOrganizationID(u uint64) *UsageStatisticDayUpdateOne {
 	usduo.mutation.ResetOrganizationID()
@@ -802,6 +839,18 @@ func (usduo *UsageStatisticDayUpdateOne) AddNewUser(i int64) *UsageStatisticDayU
 	return usduo
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usduo *UsageStatisticDayUpdateOne) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticDayUpdateOne {
+	usduo.mutation.SetLabelDist(ctd)
+	return usduo
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (usduo *UsageStatisticDayUpdateOne) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticDayUpdateOne {
+	usduo.mutation.AppendLabelDist(ctd)
+	return usduo
+}
+
 // Mutation returns the UsageStatisticDayMutation object of the builder.
 func (usduo *UsageStatisticDayUpdateOne) Mutation() *UsageStatisticDayMutation {
 	return usduo.mutation
@@ -921,6 +970,9 @@ func (usduo *UsageStatisticDayUpdateOne) sqlSave(ctx context.Context) (_node *Us
 	if value, ok := usduo.mutation.BotID(); ok {
 		_spec.SetField(usagestatisticday.FieldBotID, field.TypeString, value)
 	}
+	if usduo.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatisticday.FieldBotID, field.TypeString)
+	}
 	if value, ok := usduo.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatisticday.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -978,6 +1030,14 @@ func (usduo *UsageStatisticDayUpdateOne) sqlSave(ctx context.Context) (_node *Us
 	if value, ok := usduo.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatisticday.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := usduo.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticday.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := usduo.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatisticday.FieldLabelDist, value)
+		})
+	}
 	_node = &UsageStatisticDay{config: usduo.config}
 	_spec.Assign = _node.assignValues
 	_spec.ScanValues = _node.scanValues

+ 18 - 1
ent/usagestatistichour.go

@@ -3,9 +3,11 @@
 package ent
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatistichour"
 
 	"entgo.io/ent"
@@ -48,7 +50,9 @@ type UsageStatisticHour struct {
 	// 活跃用户数
 	ActiveUser uint64 `json:"active_user,omitempty"`
 	// 新增用户数
-	NewUser      int64 `json:"new_user,omitempty"`
+	NewUser int64 `json:"new_user,omitempty"`
+	// 标签分布
+	LabelDist    []custom_types.LabelDist `json:"label_dist,omitempty"`
 	selectValues sql.SelectValues
 }
 
@@ -57,6 +61,8 @@ func (*UsageStatisticHour) scanValues(columns []string) ([]any, error) {
 	values := make([]any, len(columns))
 	for i := range columns {
 		switch columns[i] {
+		case usagestatistichour.FieldLabelDist:
+			values[i] = new([]byte)
 		case usagestatistichour.FieldID, usagestatistichour.FieldStatus, usagestatistichour.FieldAddtime, usagestatistichour.FieldType, usagestatistichour.FieldOrganizationID, usagestatistichour.FieldAiResponse, usagestatistichour.FieldSopRun, usagestatistichour.FieldTotalFriend, usagestatistichour.FieldTotalGroup, usagestatistichour.FieldAccountBalance, usagestatistichour.FieldConsumeToken, usagestatistichour.FieldActiveUser, usagestatistichour.FieldNewUser:
 			values[i] = new(sql.NullInt64)
 		case usagestatistichour.FieldBotID:
@@ -180,6 +186,14 @@ func (ush *UsageStatisticHour) assignValues(columns []string, values []any) erro
 			} else if value.Valid {
 				ush.NewUser = value.Int64
 			}
+		case usagestatistichour.FieldLabelDist:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field label_dist", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &ush.LabelDist); err != nil {
+					return fmt.Errorf("unmarshal field label_dist: %w", err)
+				}
+			}
 		default:
 			ush.selectValues.Set(columns[i], values[i])
 		}
@@ -263,6 +277,9 @@ func (ush *UsageStatisticHour) String() string {
 	builder.WriteString(", ")
 	builder.WriteString("new_user=")
 	builder.WriteString(fmt.Sprintf("%v", ush.NewUser))
+	builder.WriteString(", ")
+	builder.WriteString("label_dist=")
+	builder.WriteString(fmt.Sprintf("%v", ush.LabelDist))
 	builder.WriteByte(')')
 	return builder.String()
 }

+ 3 - 0
ent/usagestatistichour/usagestatistichour.go

@@ -46,6 +46,8 @@ const (
 	FieldActiveUser = "active_user"
 	// FieldNewUser holds the string denoting the new_user field in the database.
 	FieldNewUser = "new_user"
+	// FieldLabelDist holds the string denoting the label_dist field in the database.
+	FieldLabelDist = "label_dist"
 	// Table holds the table name of the usagestatistichour in the database.
 	Table = "usage_statistic_hour"
 )
@@ -69,6 +71,7 @@ var Columns = []string{
 	FieldConsumeToken,
 	FieldActiveUser,
 	FieldNewUser,
+	FieldLabelDist,
 }
 
 // ValidColumn reports if the column name is valid (part of the table columns).

+ 10 - 0
ent/usagestatistichour/where.go

@@ -449,6 +449,16 @@ func BotIDHasSuffix(v string) predicate.UsageStatisticHour {
 	return predicate.UsageStatisticHour(sql.FieldHasSuffix(FieldBotID, v))
 }
 
+// BotIDIsNil applies the IsNil predicate on the "bot_id" field.
+func BotIDIsNil() predicate.UsageStatisticHour {
+	return predicate.UsageStatisticHour(sql.FieldIsNull(FieldBotID))
+}
+
+// BotIDNotNil applies the NotNil predicate on the "bot_id" field.
+func BotIDNotNil() predicate.UsageStatisticHour {
+	return predicate.UsageStatisticHour(sql.FieldNotNull(FieldBotID))
+}
+
 // BotIDEqualFold applies the EqualFold predicate on the "bot_id" field.
 func BotIDEqualFold(v string) predicate.UsageStatisticHour {
 	return predicate.UsageStatisticHour(sql.FieldEqualFold(FieldBotID, v))

+ 82 - 3
ent/usagestatistichour_create.go

@@ -7,6 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatistichour"
 
 	"entgo.io/ent/dialect/sql"
@@ -96,6 +97,14 @@ func (ushc *UsageStatisticHourCreate) SetBotID(s string) *UsageStatisticHourCrea
 	return ushc
 }
 
+// SetNillableBotID sets the "bot_id" field if the given value is not nil.
+func (ushc *UsageStatisticHourCreate) SetNillableBotID(s *string) *UsageStatisticHourCreate {
+	if s != nil {
+		ushc.SetBotID(*s)
+	}
+	return ushc
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (ushc *UsageStatisticHourCreate) SetOrganizationID(u uint64) *UsageStatisticHourCreate {
 	ushc.mutation.SetOrganizationID(u)
@@ -158,6 +167,12 @@ func (ushc *UsageStatisticHourCreate) SetNewUser(i int64) *UsageStatisticHourCre
 	return ushc
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (ushc *UsageStatisticHourCreate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticHourCreate {
+	ushc.mutation.SetLabelDist(ctd)
+	return ushc
+}
+
 // SetID sets the "id" field.
 func (ushc *UsageStatisticHourCreate) SetID(u uint64) *UsageStatisticHourCreate {
 	ushc.mutation.SetID(u)
@@ -236,9 +251,6 @@ func (ushc *UsageStatisticHourCreate) check() error {
 	if _, ok := ushc.mutation.GetType(); !ok {
 		return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "UsageStatisticHour.type"`)}
 	}
-	if _, ok := ushc.mutation.BotID(); !ok {
-		return &ValidationError{Name: "bot_id", err: errors.New(`ent: missing required field "UsageStatisticHour.bot_id"`)}
-	}
 	if _, ok := ushc.mutation.AiResponse(); !ok {
 		return &ValidationError{Name: "ai_response", err: errors.New(`ent: missing required field "UsageStatisticHour.ai_response"`)}
 	}
@@ -263,6 +275,9 @@ func (ushc *UsageStatisticHourCreate) check() error {
 	if _, ok := ushc.mutation.NewUser(); !ok {
 		return &ValidationError{Name: "new_user", err: errors.New(`ent: missing required field "UsageStatisticHour.new_user"`)}
 	}
+	if _, ok := ushc.mutation.LabelDist(); !ok {
+		return &ValidationError{Name: "label_dist", err: errors.New(`ent: missing required field "UsageStatisticHour.label_dist"`)}
+	}
 	return nil
 }
 
@@ -360,6 +375,10 @@ func (ushc *UsageStatisticHourCreate) createSpec() (*UsageStatisticHour, *sqlgra
 		_spec.SetField(usagestatistichour.FieldNewUser, field.TypeInt64, value)
 		_node.NewUser = value
 	}
+	if value, ok := ushc.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatistichour.FieldLabelDist, field.TypeJSON, value)
+		_node.LabelDist = value
+	}
 	return _node, _spec
 }
 
@@ -514,6 +533,12 @@ func (u *UsageStatisticHourUpsert) UpdateBotID() *UsageStatisticHourUpsert {
 	return u
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticHourUpsert) ClearBotID() *UsageStatisticHourUpsert {
+	u.SetNull(usagestatistichour.FieldBotID)
+	return u
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticHourUpsert) SetOrganizationID(v uint64) *UsageStatisticHourUpsert {
 	u.Set(usagestatistichour.FieldOrganizationID, v)
@@ -682,6 +707,18 @@ func (u *UsageStatisticHourUpsert) AddNewUser(v int64) *UsageStatisticHourUpsert
 	return u
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticHourUpsert) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticHourUpsert {
+	u.Set(usagestatistichour.FieldLabelDist, v)
+	return u
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticHourUpsert) UpdateLabelDist() *UsageStatisticHourUpsert {
+	u.SetExcluded(usagestatistichour.FieldLabelDist)
+	return u
+}
+
 // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
 // Using this option is equivalent to using:
 //
@@ -852,6 +889,13 @@ func (u *UsageStatisticHourUpsertOne) UpdateBotID() *UsageStatisticHourUpsertOne
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticHourUpsertOne) ClearBotID() *UsageStatisticHourUpsertOne {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticHourUpsertOne) SetOrganizationID(v uint64) *UsageStatisticHourUpsertOne {
 	return u.Update(func(s *UsageStatisticHourUpsert) {
@@ -1048,6 +1092,20 @@ func (u *UsageStatisticHourUpsertOne) UpdateNewUser() *UsageStatisticHourUpsertO
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticHourUpsertOne) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticHourUpsertOne {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticHourUpsertOne) UpdateLabelDist() *UsageStatisticHourUpsertOne {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticHourUpsertOne) Exec(ctx context.Context) error {
 	if len(u.create.conflict) == 0 {
@@ -1384,6 +1442,13 @@ func (u *UsageStatisticHourUpsertBulk) UpdateBotID() *UsageStatisticHourUpsertBu
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticHourUpsertBulk) ClearBotID() *UsageStatisticHourUpsertBulk {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticHourUpsertBulk) SetOrganizationID(v uint64) *UsageStatisticHourUpsertBulk {
 	return u.Update(func(s *UsageStatisticHourUpsert) {
@@ -1580,6 +1645,20 @@ func (u *UsageStatisticHourUpsertBulk) UpdateNewUser() *UsageStatisticHourUpsert
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticHourUpsertBulk) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticHourUpsertBulk {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticHourUpsertBulk) UpdateLabelDist() *UsageStatisticHourUpsertBulk {
+	return u.Update(func(s *UsageStatisticHourUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticHourUpsertBulk) Exec(ctx context.Context) error {
 	if u.create.err != nil {

+ 60 - 0
ent/usagestatistichour_update.go

@@ -7,11 +7,13 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/usagestatistichour"
 
 	"entgo.io/ent/dialect/sql"
 	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
 	"entgo.io/ent/schema/field"
 )
 
@@ -137,6 +139,12 @@ func (ushu *UsageStatisticHourUpdate) SetNillableBotID(s *string) *UsageStatisti
 	return ushu
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (ushu *UsageStatisticHourUpdate) ClearBotID() *UsageStatisticHourUpdate {
+	ushu.mutation.ClearBotID()
+	return ushu
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (ushu *UsageStatisticHourUpdate) SetOrganizationID(u uint64) *UsageStatisticHourUpdate {
 	ushu.mutation.ResetOrganizationID()
@@ -332,6 +340,18 @@ func (ushu *UsageStatisticHourUpdate) AddNewUser(i int64) *UsageStatisticHourUpd
 	return ushu
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (ushu *UsageStatisticHourUpdate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticHourUpdate {
+	ushu.mutation.SetLabelDist(ctd)
+	return ushu
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (ushu *UsageStatisticHourUpdate) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticHourUpdate {
+	ushu.mutation.AppendLabelDist(ctd)
+	return ushu
+}
+
 // Mutation returns the UsageStatisticHourMutation object of the builder.
 func (ushu *UsageStatisticHourUpdate) Mutation() *UsageStatisticHourMutation {
 	return ushu.mutation
@@ -421,6 +441,9 @@ func (ushu *UsageStatisticHourUpdate) sqlSave(ctx context.Context) (n int, err e
 	if value, ok := ushu.mutation.BotID(); ok {
 		_spec.SetField(usagestatistichour.FieldBotID, field.TypeString, value)
 	}
+	if ushu.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatistichour.FieldBotID, field.TypeString)
+	}
 	if value, ok := ushu.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatistichour.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -478,6 +501,14 @@ func (ushu *UsageStatisticHourUpdate) sqlSave(ctx context.Context) (n int, err e
 	if value, ok := ushu.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatistichour.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := ushu.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatistichour.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := ushu.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatistichour.FieldLabelDist, value)
+		})
+	}
 	if n, err = sqlgraph.UpdateNodes(ctx, ushu.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{usagestatistichour.Label}
@@ -607,6 +638,12 @@ func (ushuo *UsageStatisticHourUpdateOne) SetNillableBotID(s *string) *UsageStat
 	return ushuo
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (ushuo *UsageStatisticHourUpdateOne) ClearBotID() *UsageStatisticHourUpdateOne {
+	ushuo.mutation.ClearBotID()
+	return ushuo
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (ushuo *UsageStatisticHourUpdateOne) SetOrganizationID(u uint64) *UsageStatisticHourUpdateOne {
 	ushuo.mutation.ResetOrganizationID()
@@ -802,6 +839,18 @@ func (ushuo *UsageStatisticHourUpdateOne) AddNewUser(i int64) *UsageStatisticHou
 	return ushuo
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (ushuo *UsageStatisticHourUpdateOne) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticHourUpdateOne {
+	ushuo.mutation.SetLabelDist(ctd)
+	return ushuo
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (ushuo *UsageStatisticHourUpdateOne) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticHourUpdateOne {
+	ushuo.mutation.AppendLabelDist(ctd)
+	return ushuo
+}
+
 // Mutation returns the UsageStatisticHourMutation object of the builder.
 func (ushuo *UsageStatisticHourUpdateOne) Mutation() *UsageStatisticHourMutation {
 	return ushuo.mutation
@@ -921,6 +970,9 @@ func (ushuo *UsageStatisticHourUpdateOne) sqlSave(ctx context.Context) (_node *U
 	if value, ok := ushuo.mutation.BotID(); ok {
 		_spec.SetField(usagestatistichour.FieldBotID, field.TypeString, value)
 	}
+	if ushuo.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatistichour.FieldBotID, field.TypeString)
+	}
 	if value, ok := ushuo.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatistichour.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -978,6 +1030,14 @@ func (ushuo *UsageStatisticHourUpdateOne) sqlSave(ctx context.Context) (_node *U
 	if value, ok := ushuo.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatistichour.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := ushuo.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatistichour.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := ushuo.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatistichour.FieldLabelDist, value)
+		})
+	}
 	_node = &UsageStatisticHour{config: ushuo.config}
 	_spec.Assign = _node.assignValues
 	_spec.ScanValues = _node.scanValues

+ 18 - 1
ent/usagestatisticmonth.go

@@ -3,9 +3,11 @@
 package ent
 
 import (
+	"encoding/json"
 	"fmt"
 	"strings"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatisticmonth"
 
 	"entgo.io/ent"
@@ -48,7 +50,9 @@ type UsageStatisticMonth struct {
 	// 活跃用户数
 	ActiveUser uint64 `json:"active_user,omitempty"`
 	// 新增用户数
-	NewUser      int64 `json:"new_user,omitempty"`
+	NewUser int64 `json:"new_user,omitempty"`
+	// 标签分布
+	LabelDist    []custom_types.LabelDist `json:"label_dist,omitempty"`
 	selectValues sql.SelectValues
 }
 
@@ -57,6 +61,8 @@ func (*UsageStatisticMonth) scanValues(columns []string) ([]any, error) {
 	values := make([]any, len(columns))
 	for i := range columns {
 		switch columns[i] {
+		case usagestatisticmonth.FieldLabelDist:
+			values[i] = new([]byte)
 		case usagestatisticmonth.FieldID, usagestatisticmonth.FieldStatus, usagestatisticmonth.FieldAddtime, usagestatisticmonth.FieldType, usagestatisticmonth.FieldOrganizationID, usagestatisticmonth.FieldAiResponse, usagestatisticmonth.FieldSopRun, usagestatisticmonth.FieldTotalFriend, usagestatisticmonth.FieldTotalGroup, usagestatisticmonth.FieldAccountBalance, usagestatisticmonth.FieldConsumeToken, usagestatisticmonth.FieldActiveUser, usagestatisticmonth.FieldNewUser:
 			values[i] = new(sql.NullInt64)
 		case usagestatisticmonth.FieldBotID:
@@ -180,6 +186,14 @@ func (usm *UsageStatisticMonth) assignValues(columns []string, values []any) err
 			} else if value.Valid {
 				usm.NewUser = value.Int64
 			}
+		case usagestatisticmonth.FieldLabelDist:
+			if value, ok := values[i].(*[]byte); !ok {
+				return fmt.Errorf("unexpected type %T for field label_dist", values[i])
+			} else if value != nil && len(*value) > 0 {
+				if err := json.Unmarshal(*value, &usm.LabelDist); err != nil {
+					return fmt.Errorf("unmarshal field label_dist: %w", err)
+				}
+			}
 		default:
 			usm.selectValues.Set(columns[i], values[i])
 		}
@@ -263,6 +277,9 @@ func (usm *UsageStatisticMonth) String() string {
 	builder.WriteString(", ")
 	builder.WriteString("new_user=")
 	builder.WriteString(fmt.Sprintf("%v", usm.NewUser))
+	builder.WriteString(", ")
+	builder.WriteString("label_dist=")
+	builder.WriteString(fmt.Sprintf("%v", usm.LabelDist))
 	builder.WriteByte(')')
 	return builder.String()
 }

+ 3 - 0
ent/usagestatisticmonth/usagestatisticmonth.go

@@ -46,6 +46,8 @@ const (
 	FieldActiveUser = "active_user"
 	// FieldNewUser holds the string denoting the new_user field in the database.
 	FieldNewUser = "new_user"
+	// FieldLabelDist holds the string denoting the label_dist field in the database.
+	FieldLabelDist = "label_dist"
 	// Table holds the table name of the usagestatisticmonth in the database.
 	Table = "usage_statistic_month"
 )
@@ -69,6 +71,7 @@ var Columns = []string{
 	FieldConsumeToken,
 	FieldActiveUser,
 	FieldNewUser,
+	FieldLabelDist,
 }
 
 // ValidColumn reports if the column name is valid (part of the table columns).

+ 10 - 0
ent/usagestatisticmonth/where.go

@@ -449,6 +449,16 @@ func BotIDHasSuffix(v string) predicate.UsageStatisticMonth {
 	return predicate.UsageStatisticMonth(sql.FieldHasSuffix(FieldBotID, v))
 }
 
+// BotIDIsNil applies the IsNil predicate on the "bot_id" field.
+func BotIDIsNil() predicate.UsageStatisticMonth {
+	return predicate.UsageStatisticMonth(sql.FieldIsNull(FieldBotID))
+}
+
+// BotIDNotNil applies the NotNil predicate on the "bot_id" field.
+func BotIDNotNil() predicate.UsageStatisticMonth {
+	return predicate.UsageStatisticMonth(sql.FieldNotNull(FieldBotID))
+}
+
 // BotIDEqualFold applies the EqualFold predicate on the "bot_id" field.
 func BotIDEqualFold(v string) predicate.UsageStatisticMonth {
 	return predicate.UsageStatisticMonth(sql.FieldEqualFold(FieldBotID, v))

+ 82 - 3
ent/usagestatisticmonth_create.go

@@ -7,6 +7,7 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/usagestatisticmonth"
 
 	"entgo.io/ent/dialect/sql"
@@ -96,6 +97,14 @@ func (usmc *UsageStatisticMonthCreate) SetBotID(s string) *UsageStatisticMonthCr
 	return usmc
 }
 
+// SetNillableBotID sets the "bot_id" field if the given value is not nil.
+func (usmc *UsageStatisticMonthCreate) SetNillableBotID(s *string) *UsageStatisticMonthCreate {
+	if s != nil {
+		usmc.SetBotID(*s)
+	}
+	return usmc
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usmc *UsageStatisticMonthCreate) SetOrganizationID(u uint64) *UsageStatisticMonthCreate {
 	usmc.mutation.SetOrganizationID(u)
@@ -158,6 +167,12 @@ func (usmc *UsageStatisticMonthCreate) SetNewUser(i int64) *UsageStatisticMonthC
 	return usmc
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usmc *UsageStatisticMonthCreate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticMonthCreate {
+	usmc.mutation.SetLabelDist(ctd)
+	return usmc
+}
+
 // SetID sets the "id" field.
 func (usmc *UsageStatisticMonthCreate) SetID(u uint64) *UsageStatisticMonthCreate {
 	usmc.mutation.SetID(u)
@@ -236,9 +251,6 @@ func (usmc *UsageStatisticMonthCreate) check() error {
 	if _, ok := usmc.mutation.GetType(); !ok {
 		return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "UsageStatisticMonth.type"`)}
 	}
-	if _, ok := usmc.mutation.BotID(); !ok {
-		return &ValidationError{Name: "bot_id", err: errors.New(`ent: missing required field "UsageStatisticMonth.bot_id"`)}
-	}
 	if _, ok := usmc.mutation.AiResponse(); !ok {
 		return &ValidationError{Name: "ai_response", err: errors.New(`ent: missing required field "UsageStatisticMonth.ai_response"`)}
 	}
@@ -263,6 +275,9 @@ func (usmc *UsageStatisticMonthCreate) check() error {
 	if _, ok := usmc.mutation.NewUser(); !ok {
 		return &ValidationError{Name: "new_user", err: errors.New(`ent: missing required field "UsageStatisticMonth.new_user"`)}
 	}
+	if _, ok := usmc.mutation.LabelDist(); !ok {
+		return &ValidationError{Name: "label_dist", err: errors.New(`ent: missing required field "UsageStatisticMonth.label_dist"`)}
+	}
 	return nil
 }
 
@@ -360,6 +375,10 @@ func (usmc *UsageStatisticMonthCreate) createSpec() (*UsageStatisticMonth, *sqlg
 		_spec.SetField(usagestatisticmonth.FieldNewUser, field.TypeInt64, value)
 		_node.NewUser = value
 	}
+	if value, ok := usmc.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticmonth.FieldLabelDist, field.TypeJSON, value)
+		_node.LabelDist = value
+	}
 	return _node, _spec
 }
 
@@ -514,6 +533,12 @@ func (u *UsageStatisticMonthUpsert) UpdateBotID() *UsageStatisticMonthUpsert {
 	return u
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticMonthUpsert) ClearBotID() *UsageStatisticMonthUpsert {
+	u.SetNull(usagestatisticmonth.FieldBotID)
+	return u
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticMonthUpsert) SetOrganizationID(v uint64) *UsageStatisticMonthUpsert {
 	u.Set(usagestatisticmonth.FieldOrganizationID, v)
@@ -682,6 +707,18 @@ func (u *UsageStatisticMonthUpsert) AddNewUser(v int64) *UsageStatisticMonthUpse
 	return u
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticMonthUpsert) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticMonthUpsert {
+	u.Set(usagestatisticmonth.FieldLabelDist, v)
+	return u
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticMonthUpsert) UpdateLabelDist() *UsageStatisticMonthUpsert {
+	u.SetExcluded(usagestatisticmonth.FieldLabelDist)
+	return u
+}
+
 // UpdateNewValues updates the mutable fields using the new values that were set on create except the ID field.
 // Using this option is equivalent to using:
 //
@@ -852,6 +889,13 @@ func (u *UsageStatisticMonthUpsertOne) UpdateBotID() *UsageStatisticMonthUpsertO
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticMonthUpsertOne) ClearBotID() *UsageStatisticMonthUpsertOne {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticMonthUpsertOne) SetOrganizationID(v uint64) *UsageStatisticMonthUpsertOne {
 	return u.Update(func(s *UsageStatisticMonthUpsert) {
@@ -1048,6 +1092,20 @@ func (u *UsageStatisticMonthUpsertOne) UpdateNewUser() *UsageStatisticMonthUpser
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticMonthUpsertOne) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticMonthUpsertOne {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticMonthUpsertOne) UpdateLabelDist() *UsageStatisticMonthUpsertOne {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticMonthUpsertOne) Exec(ctx context.Context) error {
 	if len(u.create.conflict) == 0 {
@@ -1384,6 +1442,13 @@ func (u *UsageStatisticMonthUpsertBulk) UpdateBotID() *UsageStatisticMonthUpsert
 	})
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (u *UsageStatisticMonthUpsertBulk) ClearBotID() *UsageStatisticMonthUpsertBulk {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.ClearBotID()
+	})
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (u *UsageStatisticMonthUpsertBulk) SetOrganizationID(v uint64) *UsageStatisticMonthUpsertBulk {
 	return u.Update(func(s *UsageStatisticMonthUpsert) {
@@ -1580,6 +1645,20 @@ func (u *UsageStatisticMonthUpsertBulk) UpdateNewUser() *UsageStatisticMonthUpse
 	})
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (u *UsageStatisticMonthUpsertBulk) SetLabelDist(v []custom_types.LabelDist) *UsageStatisticMonthUpsertBulk {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.SetLabelDist(v)
+	})
+}
+
+// UpdateLabelDist sets the "label_dist" field to the value that was provided on create.
+func (u *UsageStatisticMonthUpsertBulk) UpdateLabelDist() *UsageStatisticMonthUpsertBulk {
+	return u.Update(func(s *UsageStatisticMonthUpsert) {
+		s.UpdateLabelDist()
+	})
+}
+
 // Exec executes the query.
 func (u *UsageStatisticMonthUpsertBulk) Exec(ctx context.Context) error {
 	if u.create.err != nil {

+ 60 - 0
ent/usagestatisticmonth_update.go

@@ -7,11 +7,13 @@ import (
 	"errors"
 	"fmt"
 	"time"
+	"wechat-api/ent/custom_types"
 	"wechat-api/ent/predicate"
 	"wechat-api/ent/usagestatisticmonth"
 
 	"entgo.io/ent/dialect/sql"
 	"entgo.io/ent/dialect/sql/sqlgraph"
+	"entgo.io/ent/dialect/sql/sqljson"
 	"entgo.io/ent/schema/field"
 )
 
@@ -137,6 +139,12 @@ func (usmu *UsageStatisticMonthUpdate) SetNillableBotID(s *string) *UsageStatist
 	return usmu
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (usmu *UsageStatisticMonthUpdate) ClearBotID() *UsageStatisticMonthUpdate {
+	usmu.mutation.ClearBotID()
+	return usmu
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usmu *UsageStatisticMonthUpdate) SetOrganizationID(u uint64) *UsageStatisticMonthUpdate {
 	usmu.mutation.ResetOrganizationID()
@@ -332,6 +340,18 @@ func (usmu *UsageStatisticMonthUpdate) AddNewUser(i int64) *UsageStatisticMonthU
 	return usmu
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usmu *UsageStatisticMonthUpdate) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticMonthUpdate {
+	usmu.mutation.SetLabelDist(ctd)
+	return usmu
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (usmu *UsageStatisticMonthUpdate) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticMonthUpdate {
+	usmu.mutation.AppendLabelDist(ctd)
+	return usmu
+}
+
 // Mutation returns the UsageStatisticMonthMutation object of the builder.
 func (usmu *UsageStatisticMonthUpdate) Mutation() *UsageStatisticMonthMutation {
 	return usmu.mutation
@@ -421,6 +441,9 @@ func (usmu *UsageStatisticMonthUpdate) sqlSave(ctx context.Context) (n int, err
 	if value, ok := usmu.mutation.BotID(); ok {
 		_spec.SetField(usagestatisticmonth.FieldBotID, field.TypeString, value)
 	}
+	if usmu.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatisticmonth.FieldBotID, field.TypeString)
+	}
 	if value, ok := usmu.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatisticmonth.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -478,6 +501,14 @@ func (usmu *UsageStatisticMonthUpdate) sqlSave(ctx context.Context) (n int, err
 	if value, ok := usmu.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatisticmonth.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := usmu.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticmonth.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := usmu.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatisticmonth.FieldLabelDist, value)
+		})
+	}
 	if n, err = sqlgraph.UpdateNodes(ctx, usmu.driver, _spec); err != nil {
 		if _, ok := err.(*sqlgraph.NotFoundError); ok {
 			err = &NotFoundError{usagestatisticmonth.Label}
@@ -607,6 +638,12 @@ func (usmuo *UsageStatisticMonthUpdateOne) SetNillableBotID(s *string) *UsageSta
 	return usmuo
 }
 
+// ClearBotID clears the value of the "bot_id" field.
+func (usmuo *UsageStatisticMonthUpdateOne) ClearBotID() *UsageStatisticMonthUpdateOne {
+	usmuo.mutation.ClearBotID()
+	return usmuo
+}
+
 // SetOrganizationID sets the "organization_id" field.
 func (usmuo *UsageStatisticMonthUpdateOne) SetOrganizationID(u uint64) *UsageStatisticMonthUpdateOne {
 	usmuo.mutation.ResetOrganizationID()
@@ -802,6 +839,18 @@ func (usmuo *UsageStatisticMonthUpdateOne) AddNewUser(i int64) *UsageStatisticMo
 	return usmuo
 }
 
+// SetLabelDist sets the "label_dist" field.
+func (usmuo *UsageStatisticMonthUpdateOne) SetLabelDist(ctd []custom_types.LabelDist) *UsageStatisticMonthUpdateOne {
+	usmuo.mutation.SetLabelDist(ctd)
+	return usmuo
+}
+
+// AppendLabelDist appends ctd to the "label_dist" field.
+func (usmuo *UsageStatisticMonthUpdateOne) AppendLabelDist(ctd []custom_types.LabelDist) *UsageStatisticMonthUpdateOne {
+	usmuo.mutation.AppendLabelDist(ctd)
+	return usmuo
+}
+
 // Mutation returns the UsageStatisticMonthMutation object of the builder.
 func (usmuo *UsageStatisticMonthUpdateOne) Mutation() *UsageStatisticMonthMutation {
 	return usmuo.mutation
@@ -921,6 +970,9 @@ func (usmuo *UsageStatisticMonthUpdateOne) sqlSave(ctx context.Context) (_node *
 	if value, ok := usmuo.mutation.BotID(); ok {
 		_spec.SetField(usagestatisticmonth.FieldBotID, field.TypeString, value)
 	}
+	if usmuo.mutation.BotIDCleared() {
+		_spec.ClearField(usagestatisticmonth.FieldBotID, field.TypeString)
+	}
 	if value, ok := usmuo.mutation.OrganizationID(); ok {
 		_spec.SetField(usagestatisticmonth.FieldOrganizationID, field.TypeUint64, value)
 	}
@@ -978,6 +1030,14 @@ func (usmuo *UsageStatisticMonthUpdateOne) sqlSave(ctx context.Context) (_node *
 	if value, ok := usmuo.mutation.AddedNewUser(); ok {
 		_spec.AddField(usagestatisticmonth.FieldNewUser, field.TypeInt64, value)
 	}
+	if value, ok := usmuo.mutation.LabelDist(); ok {
+		_spec.SetField(usagestatisticmonth.FieldLabelDist, field.TypeJSON, value)
+	}
+	if value, ok := usmuo.mutation.AppendedLabelDist(); ok {
+		_spec.AddModifier(func(u *sql.UpdateBuilder) {
+			sqljson.Append(u, usagestatisticmonth.FieldLabelDist, value)
+		})
+	}
 	_node = &UsageStatisticMonth{config: usmuo.config}
 	_spec.Assign = _node.assignValues
 	_spec.ScanValues = _node.scanValues

+ 0 - 87
etc/wechat-docker.yaml

@@ -1,87 +0,0 @@
-Name: Wechat.api
-Host: 0.0.0.0
-Port: 19101
-Timeout: 30000
-
-Mode: "dev"
-
-Auth:
-  AccessSecret: LnQD46hBde0AgFXBer8ZZZe3FgC
-  AccessExpire: 259200
-
-CROSConf:
-  Address: '*'
-
-Log:
-  ServiceName: WechatApiLogger
-  Mode: console
-  Encoding: plain
-  Stat: false
-  Path: /tmp
-  Level: info
-  Compress: false
-  KeepDays: 7
-  StackCoolDownMillis: 100
-
-DatabaseConf:
-  Type: mysql
-  Host: scrm-mysql
-  Port: 3306
-  DBName: wechat
-  Username: root
-  Password: simple-admin.
-  MaxOpenConn: 100
-  SSLMode: disable
-  CacheTime: 5
-
-CoreRpc:
-  # Target: k8s://default/core-rpc-svc:9101
-  Endpoints:
-    - core-rpc:9101
-  Enabled: true
-
-RedisConf:
-  Host: scrm-redis:6379
-
-CasbinDatabaseConf:
-  Type: mysql
-  Host: scrm-mysql
-  Port: 3306
-  DBName: wechat-admin
-  Username: root
-  Password: simple-admin.
-  MaxOpenConn: 100
-  SSLMode: disable
-  CacheTime: 5
-
-CasbinConf:
-  ModelText: |
-    [request_definition]
-    r = sub, obj, act
-    [policy_definition]
-    p = sub, obj, act
-    [role_definition]
-    g = _, _
-    [policy_effect]
-    e = some(where (p.eft == allow))
-    [matchers]
-    m = r.sub == p.sub && keyMatch2(r.obj,p.obj) && r.act == p.act
-
-Miniprogram:
-  Appid: wx1452f34bba8fe718
-  Secret: 171fdab212fdde0d51b59fa59c9ee070
-  redisaddr: scrm-redis:6379
-
-Aliyun:
-  ACCESS_KEY_ID: LTAI5tSJwCQyuaxXR3UxfnWw
-  ACCESS_KEY_SECRET: 0pv4xhSPJv9IPSxrkB52FspJk27W7V
-  OSS_ENDPOINT: sts.cn-beijing.aliyuncs.com
-  OSS_ROLEARN: acs:ram::1317798064750399:role/ramoss
-
-Fastgpt:
-  BASE_URL: http://new-api.gkscrm.com/v1
-  API_KEY: sk-ZQRNypQOC8ID5WbpCdF263C58dF44271842e86D408Bb3848
-
-Xiaoice:
-  SubscriptionKey: ccaa5a6ff70f4393a934e69b9ace31bb
-  GptbotsAuthorization: app-hQL7oVq57McK5VBHlsMfhtUD

+ 116 - 154
internal/logic/dashboard/get_charts_logic.go

@@ -6,7 +6,8 @@ import (
 	"github.com/suyuan32/simple-admin-common/msg/errormsg"
 	"strconv"
 	"time"
-	"wechat-api/ent"
+	"wechat-api/ent/custom_types"
+	"wechat-api/ent/label"
 	"wechat-api/ent/usagestatisticday"
 	"wechat-api/ent/usagestatistichour"
 	"wechat-api/internal/svc"
@@ -93,46 +94,15 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 	}
 
 	// 定义变量
-	aiResponse := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	sopRun := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	totalFriend := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	totalGroup := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	accountBalance := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	consumeToken := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	activeUser := types.ChartsUint{
-		Count: 0,
-		Val:   []uint64{}, // 初始化切片
-	}
-	newUser := types.ChartsInt{
-		Count: 0,
-		Val:   []int64{}, // 初始化切片
-	}
-
-	var aiResponseSum []SumUint
-	var sopRunSum []SumUint
-	var totalFriendSum []SumUint
-	var totalGroupSum []SumUint
-	var consumeTokenSum []SumUint
-	var activeUserSum []SumUint
-	var newUserSum []SumInt
+	aiResponse := types.ChartsUint{}
+	sopRun := types.ChartsUint{}
+	totalFriend := types.ChartsUint{}
+	totalGroup := types.ChartsUint{}
+	accountBalance := types.ChartsUint{}
+	consumeToken := types.ChartsUint{}
+	activeUser := types.ChartsUint{}
+	newUser := types.ChartsInt{}
+	var labelDists []custom_types.LabelDist
 
 	if isCurrentDay && layoutsType == 1 && req.StartDate == req.EndDate {
 		// 返回当日每小时的数据
@@ -143,64 +113,55 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 			fmt.Println("转换开始时间失败:", err)
 			return nil, err
 		}
-		usageStatisticHour := l.svcCtx.DB.UsageStatisticHour.Query().Where(
+		usageStatisticHour, err := l.svcCtx.DB.UsageStatisticHour.Query().Where(
 			usagestatistichour.OrganizationID(organizationId),
+			usagestatistichour.BotIDIsNil(),
 			usagestatistichour.AddtimeGTE(startAddTime),
-		).GroupBy(usagestatistichour.FieldAddtime)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldAiResponse), "value")).Scan(l.ctx, &aiResponseSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldSopRun), "value")).Scan(l.ctx, &sopRunSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldTotalFriend), "value")).Scan(l.ctx, &totalFriendSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldTotalGroup), "value")).Scan(l.ctx, &totalGroupSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldConsumeToken), "value")).Scan(l.ctx, &consumeTokenSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldActiveUser), "value")).Scan(l.ctx, &activeUserSum)
-		_ = usageStatisticHour.Aggregate(ent.As(ent.Sum(usagestatistichour.FieldNewUser), "value")).Scan(l.ctx, &newUserSum)
-		for _, ar := range aiResponseSum {
-			aiResponse.Count = aiResponse.Count + ar.Value
-			aiResponse.Val = append(aiResponse.Val, ar.Value)
-		}
-		for _, sr := range sopRunSum {
-			sopRun.Count = sopRun.Count + sr.Value
-			sopRun.Val = append(sopRun.Val, sr.Value)
-		}
-		for _, tf := range totalFriendSum {
-			totalFriend.Count = tf.Value
-			totalFriend.Val = append(totalFriend.Val, tf.Value)
-		}
-		totalFriendLen := len(totalFriendSum)
-		if totalFriendLen > 0 && totalFriendSum[0].Value > 0 {
-			totalFriend.Rate = float32((totalFriendSum[totalFriendLen-1].Value - totalFriendSum[0].Value) / totalFriendSum[0].Value)
-		}
-		for _, tg := range totalGroupSum {
-			totalGroup.Count = tg.Value
-			totalGroup.Val = append(totalGroup.Val, tg.Value)
-		}
-		totalGroupLen := len(totalGroupSum)
-		if totalGroupLen > 0 && totalGroupSum[0].Value > 0 {
-			totalGroup.Rate = float32((totalGroupSum[totalGroupLen-1].Value - totalGroupSum[0].Value) / totalGroupSum[0].Value)
+		).All(l.ctx)
+		if err != nil {
+			return nil, err
 		}
-		for _, ct := range consumeTokenSum {
-			addtimeLastTwoDigits := ct.Addtime % 100
-			consumeToken.Count = consumeToken.Count + ct.Value
-			consumeToken.Val = append(consumeToken.Val, ct.Value)
+		for _, hourData := range usageStatisticHour {
+			addtimeLastTwoDigits := hourData.Addtime % 100
+
+			aiResponse.Count = aiResponse.Count + hourData.AiResponse
+			aiResponse.Val = append(aiResponse.Val, hourData.AiResponse)
+
+			sopRun.Count = sopRun.Count + hourData.SopRun
+			sopRun.Val = append(sopRun.Val, hourData.SopRun)
+
+			totalFriend.Count = hourData.TotalFriend
+			totalFriend.Val = append(totalFriend.Val, hourData.TotalFriend)
+
+			totalGroup.Count = hourData.TotalGroup
+			totalGroup.Val = append(totalGroup.Val, hourData.TotalGroup)
+
+			consumeToken.Count = consumeToken.Count + hourData.ConsumeToken
+			consumeToken.Val = append(consumeToken.Val, hourData.ConsumeToken)
 			consumeToken.Label = append(consumeToken.Label, fmt.Sprintf("%02d", addtimeLastTwoDigits))
-		}
-		for _, au := range activeUserSum {
-			activeUser.Count = au.Value
-			activeUser.Val = append(activeUser.Val, au.Value)
-		}
-		activeUserLen := len(activeUserSum)
-		if activeUserLen > 0 && activeUserSum[0].Value > 0 {
-			activeUser.Rate = float32((activeUserSum[activeUserLen-1].Value - activeUserSum[0].Value) / activeUserSum[0].Value)
-		}
-		for _, nu := range newUserSum {
-			addtimeLastTwoDigits := nu.Addtime % 100
-			newUser.Count = nu.Value
-			newUser.Val = append(newUser.Val, nu.Value)
+
+			activeUser.Count = hourData.ActiveUser
+			activeUser.Val = append(activeUser.Val, hourData.ActiveUser)
+
+			newUser.Count = hourData.NewUser
+			newUser.Val = append(newUser.Val, hourData.NewUser)
 			newUser.Label = append(newUser.Label, fmt.Sprintf("%02d", addtimeLastTwoDigits))
 		}
-		newUserLen := len(newUserSum)
-		if newUserLen > 0 && newUserSum[0].Value > 0 {
-			newUser.Rate = float32((newUserSum[newUserLen-1].Value - newUserSum[0].Value) / newUserSum[0].Value)
+		hourLen := len(usageStatisticHour)
+		if hourLen > 0 {
+			if usageStatisticHour[0].TotalFriend > 0 {
+				totalFriend.Rate = float32((usageStatisticHour[hourLen-1].TotalFriend - usageStatisticHour[0].TotalFriend) / usageStatisticHour[0].TotalFriend)
+			}
+			if usageStatisticHour[0].TotalGroup > 0 {
+				totalGroup.Rate = float32((usageStatisticHour[hourLen-1].TotalGroup - usageStatisticHour[0].TotalGroup) / usageStatisticHour[0].TotalGroup)
+			}
+			if usageStatisticHour[0].ActiveUser > 0 {
+				activeUser.Rate = float32((usageStatisticHour[hourLen-1].ActiveUser - usageStatisticHour[0].ActiveUser) / usageStatisticHour[0].ActiveUser)
+			}
+			if usageStatisticHour[0].NewUser > 0 {
+				newUser.Rate = float32((usageStatisticHour[hourLen-1].NewUser - usageStatisticHour[0].NewUser) / usageStatisticHour[0].NewUser)
+			}
+			labelDists = usageStatisticHour[hourLen-1].LabelDist
 		}
 	} else {
 		// 从天表统计数据
@@ -216,76 +177,55 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 			fmt.Println("转换截止时间失败:", err)
 			return nil, err
 		}
-		usageStatisticDay := l.svcCtx.DB.UsageStatisticDay.Query().
+		usageStatisticDay, err := l.svcCtx.DB.UsageStatisticDay.Query().
 			Where(
 				usagestatisticday.OrganizationID(organizationId),
+				usagestatisticday.BotIDIsNil(),
 				usagestatisticday.AddtimeGTE(startAddTime),
 				usagestatisticday.AddtimeLTE(endAddTime),
-			).
-			GroupBy(usagestatisticday.FieldAddtime)
-		//count, _ := usageStatisticDay..Count(l.ctx)
-		//l.Infof("----------------count--------------: %d", count)
-		var v []SumUint
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Count(), "value")).Scan(l.ctx, &v)
-		l.Infof("----------------count--------------: %d", v)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldAiResponse), "value")).Scan(l.ctx, &aiResponseSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldSopRun), "value")).Scan(l.ctx, &sopRunSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldTotalFriend), "value")).Scan(l.ctx, &totalFriendSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldTotalGroup), "value")).Scan(l.ctx, &totalGroupSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldConsumeToken), "value")).Scan(l.ctx, &consumeTokenSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldActiveUser), "value")).Scan(l.ctx, &activeUserSum)
-		_ = usageStatisticDay.Aggregate(ent.As(ent.Sum(usagestatisticday.FieldNewUser), "value")).Scan(l.ctx, &newUserSum)
-
-		for _, ar := range aiResponseSum {
-			aiResponse.Count = aiResponse.Count + ar.Value
-			aiResponse.Val = append(aiResponse.Val, ar.Value)
-		}
-		for _, sr := range sopRunSum {
-			sopRun.Count = sopRun.Count + sr.Value
-			sopRun.Val = append(sopRun.Val, sr.Value)
+			).All(l.ctx)
+		if err != nil {
+			return nil, err
 		}
+		for _, dayData := range usageStatisticDay {
+			aiResponse.Count = aiResponse.Count + dayData.AiResponse
+			aiResponse.Val = append(aiResponse.Val, dayData.AiResponse)
 
-		for _, tf := range totalFriendSum {
-			totalFriend.Count = tf.Value
-			totalFriend.Val = append(totalFriend.Val, tf.Value)
-		}
-		totalFriendLen := len(totalFriendSum)
-		if totalFriendLen > 0 && totalFriendSum[0].Value > 0 {
-			totalFriend.Rate = float32((totalFriendSum[totalFriendLen-1].Value - totalFriendSum[0].Value) / totalFriendSum[0].Value)
-		}
+			sopRun.Count = sopRun.Count + dayData.SopRun
+			sopRun.Val = append(sopRun.Val, dayData.SopRun)
 
-		for _, tg := range totalGroupSum {
-			totalGroup.Count = tg.Value
-			totalGroup.Val = append(totalGroup.Val, tg.Value)
-		}
-		totalGroupLen := len(totalGroupSum)
-		if totalGroupLen > 0 && totalGroupSum[0].Value > 0 {
-			totalGroup.Rate = float32((totalGroupSum[totalGroupLen-1].Value - totalGroupSum[0].Value) / totalGroupSum[0].Value)
-		}
+			totalFriend.Count = dayData.TotalFriend
+			totalFriend.Val = append(totalFriend.Val, dayData.TotalFriend)
 
-		for _, ct := range consumeTokenSum {
-			//addtimeLastTwoDigits := ct.Addtime % 100
-			consumeToken.Count = consumeToken.Count + ct.Value
-			consumeToken.Val = append(consumeToken.Val, ct.Value)
-			consumeToken.Label = append(consumeToken.Label, fmt.Sprintf("%d", ct.Addtime))
-		}
-		for _, au := range activeUserSum {
-			activeUser.Count = au.Value
-			activeUser.Val = append(activeUser.Val, au.Value)
-		}
-		activeUserLen := len(activeUserSum)
-		if activeUserLen > 0 && activeUserSum[0].Value > 0 {
-			activeUser.Rate = float32((activeUserSum[activeUserLen-1].Value - activeUserSum[0].Value) / activeUserSum[0].Value)
-		}
-		for _, nu := range newUserSum {
-			addtimeLastTwoDigits := nu.Addtime % 100
-			newUser.Count = nu.Value
-			newUser.Val = append(newUser.Val, nu.Value)
-			newUser.Label = append(newUser.Label, fmt.Sprintf("%02d", addtimeLastTwoDigits))
+			totalGroup.Count = dayData.TotalGroup
+			totalGroup.Val = append(totalGroup.Val, dayData.TotalGroup)
+
+			consumeToken.Count = consumeToken.Count + dayData.ConsumeToken
+			consumeToken.Val = append(consumeToken.Val, dayData.ConsumeToken)
+			consumeToken.Label = append(consumeToken.Label, fmt.Sprintf("%d", dayData.Addtime))
+
+			activeUser.Count = dayData.ActiveUser
+			activeUser.Val = append(activeUser.Val, dayData.ActiveUser)
+
+			newUser.Count = dayData.NewUser
+			newUser.Val = append(newUser.Val, dayData.NewUser)
+			newUser.Label = append(newUser.Label, fmt.Sprintf("%d", dayData.Addtime))
 		}
-		newUserLen := len(newUserSum)
-		if newUserLen > 0 && newUserSum[0].Value > 0 {
-			newUser.Rate = float32((newUserSum[newUserLen-1].Value - newUserSum[0].Value) / newUserSum[0].Value)
+		dayLen := len(usageStatisticDay)
+		if dayLen > 0 {
+			if usageStatisticDay[0].TotalFriend > 0 {
+				totalFriend.Rate = float32((usageStatisticDay[dayLen-1].TotalFriend - usageStatisticDay[0].TotalFriend) / usageStatisticDay[0].TotalFriend)
+			}
+			if usageStatisticDay[0].TotalGroup > 0 {
+				totalGroup.Rate = float32((usageStatisticDay[dayLen-1].TotalGroup - usageStatisticDay[0].TotalGroup) / usageStatisticDay[0].TotalGroup)
+			}
+			if usageStatisticDay[0].ActiveUser > 0 {
+				activeUser.Rate = float32((usageStatisticDay[dayLen-1].ActiveUser - usageStatisticDay[0].ActiveUser) / usageStatisticDay[0].ActiveUser)
+			}
+			if usageStatisticDay[0].NewUser > 0 {
+				newUser.Rate = float32((usageStatisticDay[dayLen-1].NewUser - usageStatisticDay[0].NewUser) / usageStatisticDay[0].NewUser)
+			}
+			labelDists = usageStatisticDay[dayLen-1].LabelDist
 		}
 
 		if isCurrentDay {
@@ -299,6 +239,7 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 			}
 			usageStatisticHour, err := l.svcCtx.DB.UsageStatisticHour.Query().Where(
 				usagestatistichour.OrganizationID(organizationId),
+				usagestatistichour.BotIDIsNil(),
 				usagestatistichour.AddtimeGTE(startOfDayAddTime),
 			).All(l.ctx)
 			if err != nil {
@@ -362,10 +303,30 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 				if nuLen > 0 && newUser.Val[0] > 0 {
 					newUser.Rate = float32((newUser.Val[nuLen-1] - newUser.Val[0]) / newUser.Val[0])
 				}
+
+				labelDists = usageStatisticHour[hourLen-1].LabelDist
 			}
 		}
 	}
 
+	var labelIds []uint64
+	var labelsData []types.LabelsData
+	labelSet := make(map[uint64]uint64)
+	for _, labelDist := range labelDists {
+		if labelDist.Count != 0 {
+			labelIds = append(labelIds, labelDist.LabelID)
+			labelSet[labelDist.LabelID] = labelDist.Count
+		}
+	}
+
+	labelInfos, err := l.svcCtx.DB.Label.Query().Where(label.IDIn(labelIds...)).All(l.ctx)
+	for _, labelInfo := range labelInfos {
+		labelsData = append(labelsData, types.LabelsData{
+			Value: labelSet[labelInfo.ID],
+			Name:  labelInfo.Name,
+		})
+	}
+
 	chartsData := types.ChartsData{
 		AiResponse:     &aiResponse,
 		SopRun:         &sopRun,
@@ -375,6 +336,7 @@ func (l *GetChartsLogic) GetCharts(req *types.ChartsReq) (resp *types.ChartsResp
 		ConsumeToken:   &consumeToken,
 		ActiveUser:     &activeUser,
 		NewUser:        &newUser,
+		LabelDist:      labelsData,
 	}
 
 	return &types.ChartsResp{BaseDataInfo: types.BaseDataInfo{Msg: errormsg.UpdateSuccess}, Data: &chartsData}, nil

+ 136 - 2
internal/logic/dashboard/get_wxs_logic.go

@@ -2,6 +2,15 @@ package dashboard
 
 import (
 	"context"
+	"fmt"
+	"github.com/suyuan32/simple-admin-common/msg/errormsg"
+	"github.com/suyuan32/simple-admin-common/utils/pointy"
+	"time"
+	"wechat-api/ent"
+	"wechat-api/ent/usagestatisticday"
+	"wechat-api/ent/usagestatistichour"
+	"wechat-api/ent/wx"
+	"wechat-api/internal/utils/dberrorhandler"
 
 	"wechat-api/internal/svc"
 	"wechat-api/internal/types"
@@ -23,7 +32,132 @@ func NewGetWxsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWxsLogi
 }
 
 func (l *GetWxsLogic) GetWxs(req *types.WxReq) (resp *types.WxResp, err error) {
-	// todo: add your logic here and delete this line
+	// 获取组织id
+	var organizationId uint64 = 0
+	isAdmin := l.ctx.Value("isAdmin").(bool)
+	if isAdmin && req.OrganizationId != nil && *req.OrganizationId != 0 {
+		organizationId = *req.OrganizationId
+	} else {
+		organizationId = l.ctx.Value("organizationId").(uint64)
+	}
 
-	return
+	// 解析起始和截止时间
+	layouts := []string{
+		"2006-01",    // 对应 "2024-01"
+		"2006-01-02", // 对应 "2024-01-01"
+	}
+
+	var layoutsType int
+
+	var endTime time.Time
+	for i, layout := range layouts {
+		endTime, err = time.Parse(layout, *req.EndDate)
+		layoutsType = i
+		if err == nil {
+			break
+		}
+	}
+	if err != nil {
+		fmt.Println("解析结束时间失败:", err)
+		return
+	}
+
+	// 判断截止日期是否包含当前日
+	var isCurrentDay bool
+	now := time.Now()
+
+	if layoutsType == 0 {
+		isCurrentDay = endTime.Year() == now.Year() && endTime.Month() == now.Month()
+	} else {
+		isCurrentDay = endTime.Year() == now.Year() && endTime.Month() == now.Month() && endTime.Day() == now.Day()
+	}
+
+	wxs, err := l.svcCtx.DB.Wx.Query().Where(wx.OrganizationID(organizationId)).Page(l.ctx, req.Page, req.PageSize)
+
+	if err != nil {
+		return nil, dberrorhandler.DefaultEntError(l.Logger, err, req)
+	}
+
+	resp := &types.WxResp{}
+	resp.Msg = errormsg.Success
+	resp.Data.Total = wxs.PageDetails.Total
+
+	var wxIds []string
+	wxSet := make(map[string]*ent.Wx)
+	for _, w := range wxs.List {
+		wxIds = append(wxIds, w.Wxid)
+		wxSet[w.Wxid] = w
+	}
+
+	var wxList []types.WxData
+
+	if isCurrentDay {
+		// 返回当日每小时的数据
+		lastHourData, err := l.svcCtx.DB.UsageStatisticHour.Query().
+			Where(
+				usagestatistichour.OrganizationID(organizationId),
+				usagestatistichour.BotIDIn(wxIds...),
+			).
+			Order(ent.Desc(usagestatistichour.FieldAddtime)).
+			First(l.ctx)
+		if err != nil {
+			return nil, err
+		}
+		if lastHourData == nil {
+			return nil, err
+		}
+		usageStatisticHour, err := l.svcCtx.DB.UsageStatisticHour.Query().
+			Where(
+				usagestatistichour.OrganizationID(organizationId),
+				usagestatistichour.BotIDIn(wxIds...),
+				usagestatistichour.Addtime(lastHourData.Addtime),
+			).All(l.ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, hourData := range usageStatisticHour {
+			wxList = append(wxList, types.WxData{
+				Nickname:        wxSet[hourData.BotID].Nickname,
+				InteractionRate: float32(hourData.ActiveUser) / float32(hourData.TotalFriend),
+				TotalFriend:     hourData.TotalFriend,
+				TotalGroup:      hourData.TotalGroup,
+			})
+		}
+	} else {
+		// 返回当日每小时的数据
+		lastDayData, err := l.svcCtx.DB.UsageStatisticDay.Query().
+			Where(
+				usagestatisticday.OrganizationID(organizationId),
+				usagestatisticday.BotIDIn(wxIds...),
+			).
+			Order(ent.Desc(usagestatisticday.FieldAddtime)).
+			First(l.ctx)
+		if err != nil {
+			return nil, err
+		}
+		if lastDayData == nil {
+			return nil, err
+		}
+		usageStatisticDay, err := l.svcCtx.DB.UsageStatisticDay.Query().
+			Where(
+				usagestatisticday.OrganizationID(organizationId),
+				usagestatisticday.BotIDIn(wxIds...),
+				usagestatisticday.Addtime(lastDayData.Addtime),
+			).All(l.ctx)
+		if err != nil {
+			return nil, err
+		}
+		for _, dayData := range usageStatisticDay {
+			wxList = append(wxList, types.WxData{
+				Nickname:        wxSet[dayData.BotID].Nickname,
+				InteractionRate: float32(dayData.ActiveUser) / float32(dayData.TotalFriend),
+				TotalFriend:     dayData.TotalFriend,
+				TotalGroup:      dayData.TotalGroup,
+			})
+		}
+	}
+
+	resp.Data.Data = wxList
+
+	return resp, nil
 }

+ 2 - 1
internal/types/types.go

@@ -2961,7 +2961,7 @@ type WxReq struct {
 // swagger:model WxResp
 type WxResp struct {
 	BaseDataInfo
-	Data []WxList `json:"data"`
+	Data WxList `json:"data"`
 }
 
 type WxList struct {
@@ -2970,6 +2970,7 @@ type WxList struct {
 }
 
 type WxData struct {
+	Nickname        string  `json:"nickname"`
 	TotalFriend     uint64  `json:"total_friend"`
 	TotalGroup      uint64  `json:"total_group"`
 	InteractionRate float32 `json:"interaction_rate"`