59eb34976a
iotlcm-admin-svr:管理后台服务。 - Go module:iotlcm → git.5iot.com/iotlcm-public/iotlcm-admin-svr(729 文件) - 凭据与地址脱敏:JWT signingKey(含代码里的 MustGet 默认值)、 MySQL DSN、Redis 口令、告警邮件发件人邮箱全部改为占位; 私网 IP → 文档地址段;库名 xiujie_admin → iotlcm - 真实手机号/邮箱 → 合法 11 位编造号 / example.com - 移除内部资产:CI 触发器、过时 DDL(sql/db_iotlcm.sql)、 私有依赖 go get 行、明文 registry 登录口令 - 启动 banner 统一 IOTLCM 方块字 + 社区惯例署名 - Dockerfile 改用公共基础镜像 - 拼错的 RADEME.md → README.md,README 重写 - 需要真实数据库的用例改为可用 IOTLCM_TEST_WITH_DB=1 显式启用 - 补 Apache-2.0 LICENSE,LF 行尾 + .gitattributes 数据库 schema 不随本仓发布(生产 DDL 已过时),README 已说明。
249 lines
7.4 KiB
Go
249 lines
7.4 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
|
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/service"
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
)
|
|
|
|
type sHome struct{}
|
|
|
|
func NewHome() *sHome {
|
|
return &sHome{}
|
|
}
|
|
func init() {
|
|
service.RegisterHome(NewHome())
|
|
}
|
|
|
|
func (s *sHome) GetCardStatistics(ctx context.Context) (stats *model.CardStatistics, err error) {
|
|
stats = new(model.CardStatistics)
|
|
deptId := contexts.GetDeptId(ctx)
|
|
table := dao.TSimCardInfo.Table()
|
|
|
|
// 1. TotalCount - 卡总数
|
|
totalCount, err := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stats.TotalCount = int64(totalCount)
|
|
|
|
// 正确的日期处理:先转为本月1号,再加1个月,彻底规避月底溢出 Bug
|
|
now := gtime.Now()
|
|
monthStartStr := now.Format("Y-m-01 00:00:00")
|
|
monthEndStr := now.AddDate(0, 1, 0).Format("Y-m-01 00:00:00")
|
|
|
|
// 2. MonthlyNewCount - 本月新增
|
|
monthlyNewCount, err := g.DB().Model(table).Ctx(ctx).
|
|
Where("dept_id", deptId).
|
|
Where("boss_activate_at >= ?", monthStartStr).
|
|
Where("boss_activate_at < ?", monthEndStr).
|
|
Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stats.MonthlyNewCount = int64(monthlyNewCount)
|
|
|
|
// 3. MonthlyFlowUsage - 本月流量使用
|
|
var flowResult struct {
|
|
Total float64
|
|
}
|
|
err = g.DB().Model(table).Ctx(ctx).
|
|
Fields("IFNULL(SUM(used_flow_mb), 0) as total").
|
|
Where("dept_id", deptId).
|
|
Scan(&flowResult)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stats.MonthlyFlowUsage = int64(flowResult.Total)
|
|
|
|
// 4. MonthlyActiveCount - 本月活跃卡数
|
|
monthlyActiveCount, err := g.DB().Model(table).Ctx(ctx).
|
|
Where("dept_id", deptId).
|
|
Where("used_flow_mb > 0").
|
|
Count()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stats.MonthlyActiveCount = int64(monthlyActiveCount)
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetCardStatusStatistics 获取卡状态统计
|
|
func (s *sHome) GetCardStatusStatistics(ctx context.Context) (stats *model.CardStatusStatistics, err error) {
|
|
stats = new(model.CardStatusStatistics)
|
|
deptId := contexts.GetDeptId(ctx)
|
|
table := dao.TSimCardInfo.Table()
|
|
|
|
// 卡状态统计
|
|
// 待激活(1)
|
|
pendingActivationCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 1).Count()
|
|
stats.PendingActivationCount = int64(pendingActivationCount)
|
|
|
|
// 已激活(2)
|
|
activatedCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 2).Count()
|
|
stats.ActivatedCount = int64(activatedCount)
|
|
|
|
// 停机(4)
|
|
stoppedCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 4).Count()
|
|
stats.StoppedCount = int64(stoppedCount)
|
|
|
|
// 可测试(6)
|
|
testableCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 6).Count()
|
|
stats.TestableCount = int64(testableCount)
|
|
|
|
// 库存(7)
|
|
inventoryCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 7).Count()
|
|
stats.InventoryCount = int64(inventoryCount)
|
|
|
|
// 预销户(8)
|
|
preCancelCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 8).Count()
|
|
stats.PreCancelCount = int64(preCancelCount)
|
|
|
|
// 已销户(9)
|
|
canceledCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 9).Count()
|
|
stats.CanceledCount = int64(canceledCount)
|
|
|
|
// 未知(0)
|
|
unknownCount, _ := g.DB().Model(table).Ctx(ctx).Where("dept_id", deptId).Where("boss_card_status", 0).Count()
|
|
stats.UnknownCount = int64(unknownCount)
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// GetDeptFlowUsageChart 获取部门流量使用图表数据,用于折线图展示
|
|
// period: 7d 前7天, 1m 前6个月
|
|
func (s *sHome) GetDeptFlowUsageChart(ctx context.Context, period string) (*model.FlowUsageChartData, error) {
|
|
deptId := contexts.GetDeptId(ctx)
|
|
table := dao.TDeptFlowDaily.Table()
|
|
|
|
// 使用标准库 time 获取当前时间
|
|
now := time.Now()
|
|
|
|
var (
|
|
startDateStr string
|
|
endDateStr string
|
|
groupBy string // SQL 分组字段
|
|
labels = make([]string, 0)
|
|
values = make([]int64, 0)
|
|
)
|
|
|
|
switch period {
|
|
case "7d":
|
|
// 前7天:包含今天在内的过去7天
|
|
startDateStr = now.AddDate(0, 0, -6).Format("2006-01-02")
|
|
endDateStr = now.Format("2006-01-02")
|
|
groupBy = "stat_date" // 按天分组
|
|
|
|
case "1m":
|
|
// 前6个月:从6个月前的1号开始,方便按月对齐
|
|
startDateStr = now.AddDate(0, -5, 0).Format("2006-01-01")
|
|
endDateStr = now.Format("2006-01-02")
|
|
// 使用 MySQL 的 DATE_FORMAT 函数按月聚合
|
|
groupBy = "DATE_FORMAT(stat_date, '%Y-%m')"
|
|
|
|
default:
|
|
// 默认当月(按天)
|
|
startDateStr = now.Format("2006-01-01")
|
|
endDateStr = now.Format("2006-01-02")
|
|
groupBy = "stat_date"
|
|
}
|
|
|
|
// 1. 从数据库查询聚合数据
|
|
var list []struct {
|
|
TimeKey string `json:"time_key"`
|
|
UsedFlowMb int64 `json:"used_flow_mb"`
|
|
}
|
|
|
|
fields := fmt.Sprintf("%s as time_key, IFNULL(SUM(total_used_flow_mb), 0) as used_flow_mb", groupBy)
|
|
|
|
err := g.DB().Model(table).Ctx(ctx).
|
|
Fields(fields).
|
|
Where("dept_id", deptId).
|
|
Where("stat_date >= ? AND stat_date <= ?", startDateStr, endDateStr).
|
|
Group(groupBy).
|
|
Order("time_key ASC").
|
|
Scan(&list)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 2. 将查询结果转换为 Map,方便 O(1) 复杂度查找
|
|
dataMap := make(map[string]int64)
|
|
for _, item := range list {
|
|
dataMap[item.TimeKey] = item.UsedFlowMb
|
|
}
|
|
|
|
// 3. 核心:将字符串转回标准 time.Time 对象,用来做连续日期循环
|
|
startTime, _ := time.Parse("2006-01-02", startDateStr)
|
|
endTime, _ := time.Parse("2006-01-02", endDateStr)
|
|
|
|
if period == "1m" {
|
|
// 【6个月模式】:按月循环
|
|
for t := startTime; !t.After(endTime); t = t.AddDate(0, 1, 0) {
|
|
// 匹配数据库返回的 key 格式 "2026-06"
|
|
mapKey := t.Format("2006-01")
|
|
|
|
// 放入返回的 Labels 数组 (格式要求:2026-06)
|
|
labels = append(labels, mapKey)
|
|
|
|
// 提取数据或补 0
|
|
if val, exists := dataMap[mapKey]; exists {
|
|
values = append(values, val)
|
|
} else {
|
|
values = append(values, 0)
|
|
}
|
|
}
|
|
} else {
|
|
// 【7天/默认模式】:按天循环
|
|
for t := startTime; !t.After(endTime); t = t.AddDate(0, 0, 1) {
|
|
// 匹配数据库返回的 key 格式 "2026-06-25"
|
|
mapKey := t.Format("2006-01-02")
|
|
|
|
// 放入返回的 Labels 数组 (格式要求:06-25)
|
|
labels = append(labels, t.Format("01-02"))
|
|
|
|
// 提取数据或补 0
|
|
if val, exists := dataMap[mapKey]; exists {
|
|
values = append(values, val)
|
|
} else {
|
|
values = append(values, 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
return &model.FlowUsageChartData{
|
|
Labels: labels,
|
|
Values: values,
|
|
}, nil
|
|
}
|
|
|
|
// GetTopUsageCardsYesterday 获取前一天流量使用前五名的卡片
|
|
func (s *sHome) GetTopUsageCardsYesterday(ctx context.Context) ([]model.TopUsageCard, error) {
|
|
return service.TrafficUsage().GetTopUsageCardsYesterday(ctx)
|
|
}
|
|
|
|
// GetFlowPoolUsageList 获取该用户下所有流量池的流量使用情况
|
|
func (s *sHome) GetFlowPoolUsageList(ctx context.Context) ([]model.FlowPoolUsageItem, error) {
|
|
deptId := contexts.GetDeptId(ctx)
|
|
|
|
var pools []model.FlowPoolUsageItem
|
|
err := g.DB().Model("t_sim_flow_pool").
|
|
Fields("flow_pool_code, total_flow_mb, used_flow_mb").
|
|
Where("dept_id", deptId).
|
|
Where("pool_status", 0).
|
|
Scan(&pools)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pools, nil
|
|
}
|