# coding: utf-8
# -------------------------------------------------------------------
# YakPanel-基础网站数据统计模块
# 该模块用于统计基础网站数据，包括IP数量、流量、访问量、PV、UV等数据。
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 YakPanel(https://www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: wpl <wpl@yakpanel.com>
# -------------------------------------------------------------------
import json
import os
import re
import sys

from datetime import datetime, timedelta
from safeModel.base import safeBase
os.chdir("/www/server/panel")
sys.path.append("class_v2/")
import db
import public

class main(safeBase):
        # ------------------------ 常量设置 ------------------------
    TOTAL_DIR = '/www/server/site_total/data/total'
    SUMMARY_DIR = '/www/server/site_total/data/summary'
    def __init__(self):
        pass

    # ------------------------ 对外接口 ------------------------
    def get_overview(self, get=None):
        """
        @name 总概览接口（全站三日总览 + 网站排名TOP5 + 全站近7天趋势）
        @return public.return_data(True, data)
        安全与约束：
          - Top5站点，固定按pv升序排序（用户确认：默认升序，指标需传参，支持traffic/req_count/ip_count/uv/pv）
          - 日期以服务器本地时间为准：今日/昨日/前日
          - 近7天包含今日在内的连续7个自然日
        """
        public.set_module_logs('site_total', 'get_overview', 1)
        today, yesterday, day_before = self._get_three_days()
        # 三日总览（全站）
        today_total = self._get_all_sites_total_for_date(today)
        yesterday_total = self._get_all_sites_total_for_date(yesterday)
        day_before_total = self._get_all_sites_total_for_date(day_before)
        compare = self._compute_compare(today_total, yesterday_total)
        overview_three_days = {
            'today': self._format_daily_stat(today_total, include_compare=compare),
            'yesterday': self._format_daily_stat(yesterday_total),
            'day_before': self._format_daily_stat(day_before_total)
        }
        # Top5排序参数（支持传递，默认 pv/asc）
        metric = 'pv'
        order = 'asc'
        if get:
            try:
                gm = getattr(get, 'metric', None)
                go = getattr(get, 'order', None)
                if gm is None and isinstance(get, dict):
                    gm = get.get('metric')
                if go is None and isinstance(get, dict):
                    go = get.get('order')
                if gm:
                    metric = str(gm)
                if go:
                    order = str(go)
            except Exception:
                pass

        valid_metrics = ['traffic', 'req_count', 'ip_count', 'uv', 'pv']
        valid_orders = ['asc', 'desc']
        if metric not in valid_metrics:
            metric = 'pv'
        if order not in valid_orders:
            order = 'asc'

        # Top5 当天（支持传递 metric/order，默认 pv/asc）
        top5 = self._get_top_sites_for_date(today, metric=metric, order=order, limit=5)

        # 近7天趋势（全站）
        trend_points = self._build_trend_7days_all()
        rec_status, detail_id = self._get_rec_status_detail()
        data = {
            'date_range': {
                'today': today,
                'yesterday': yesterday,
                'day_before': day_before
            },
            'overview_three_days': overview_three_days,
            'top5_sites': {
                'metric': metric,
                'order': order,
                'timeframe': 'today',
                # 'range': {'date': today},
                'items': top5
            },
            'trend_7days': {
                'timeframe': 'last_7_days',
                'points': trend_points
            },
            'rec_status': rec_status,
            'detail_id': detail_id
        }
        return public.return_message(0, 0, data)

    def get_site_overview(self, get):
        """
        @name 指定站点数据概览接口（单站三日总览 + 单站近7天趋势）
        @param get.site_name 站点名（必填，校验：仅允许字母、数字、点、短横线、下划线）
        @return public.return_data(True, data)
        """
        # 监控报表获取数据（确保数据一致）
        public.set_module_logs('site_total', 'get_site_overview', 1)
        site_name = getattr(get, 'site_name', None)
        if not site_name:
            return public.return_message(-1, 0, 'The site name is illegal or missing!')
        today, yesterday, day_before = self._get_three_days()
        # 三日总览（单站）
        today_total = self._get_site_total_for_date(site_name, today)
        yesterday_total = self._get_site_total_for_date(site_name, yesterday)
        day_before_total = self._get_site_total_for_date(site_name, day_before)
        compare = self._compute_compare(today_total, yesterday_total)
        overview_three_days = {
            'today': self._format_daily_stat(today_total, include_compare=compare),
            'yesterday': self._format_daily_stat(yesterday_total),
            'day_before': self._format_daily_stat(day_before_total)
        }
        # 近7天趋势（单站）
        trend_points = self._build_trend_7days_site(site_name)

        # 检查config配置是否需要更新
        self._check_config()
        
        data = {
            'site': site_name,
            'date_range': {
                'today': today,
                'yesterday': yesterday,
                'day_before': day_before
            },
            'overview_three_days': overview_three_days,
            'trend_7days': {
                'site': site_name,
                'timeframe': 'last_7_days',
                'points': trend_points
            }
        }
        return public.return_message(0, 0, data)

    # def receive_products(self, get):
    #     """
    #     @name 领取产品接口
    #     @param get.detail_id 活动详情ID（必填）
    #     @return public.return_data(True, data)
    #     """
    #     try:
    #         u = public.get_user_info()
    #         if not isinstance(u, dict):
    #             return public.return_message(-1, 0, 'User information acquisition failed!')
    #         serverid = u.get('serverid')
    #         access_key = u.get('access_key')
    #         uid = u.get('uid')
    #         if not serverid or not access_key or uid is None:
    #             return public.return_message(-1, 0, 'Missing parameters')
    #         detail_id = None
    #         try:
    #             detail_id = getattr(get, 'detail_id', None)
    #         except Exception:
    #             detail_id = None
    #         if detail_id is None and isinstance(get, dict):
    #             detail_id = get.get('detail_id')
    #         if detail_id is None:
    #             return public.return_message(-1, 0, '缺少detail_id')
    #         mac = public.get_mac_address()
    #         payload = {
    #             'serverid': serverid,
    #             'access_key': access_key,
    #             'uid': uid,
    #             'detail_id': detail_id,
    #             'mac': mac
    #         }
    #         url = 'https://www.yakpanel.com/newapi/activity/panelapi/receive_products'
    #         res = public.httpPost(url, payload)
    #         if not res:
    #             return public.return_message(-1, 0, '接口请求失败')
    #         try:
    #             obj = json.loads(res)
    #         except Exception:
    #             return public.return_message(-1, 0, '响应解析失败')
    #         status = obj.get('status')
    #         success = bool(status)
    #         # 刷新软件列表状态,确保最新软件列表信息获取
    #         public.flush_plugin_list()
    #         return public.return_message(0, 0, obj)
    #     except Exception:
    #         return public.return_message(-1, 0, '领取失败')
    
    # ------------------------ 内部工具方法 ------------------------
    def _get_three_days(self):
        """返回今日、昨日、前日的日期字符串(YYYY-MM-DD)"""
        now = datetime.now()
        today = now.strftime('%Y-%m-%d')
        yesterday = (now - timedelta(days=1)).strftime('%Y-%m-%d')
        day_before = (now - timedelta(days=2)).strftime('%Y-%m-%d')
        return today, yesterday, day_before

    def _get_7day_dates(self):
        """返回近7天日期列表(包含今日), 每项格式YYYY-MM-DD"""
        base = datetime.now()
        dates = []
        for i in range(6, -1, -1):
            dates.append((base - timedelta(days=i)).strftime('%Y-%m-%d'))
        return dates

    def _get_7day_range(self):
        """返回近7天范围的字典: {start_date, end_date}"""
        base = datetime.now()
        start_date = (base - timedelta(days=6)).strftime('%Y-%m-%d')
        end_date = base.strftime('%Y-%m-%d')
        return {'start_date': start_date, 'end_date': end_date}

    def _validate_site_name(self, site):
        """校验站点名，仅允许字母、数字、点、短横线、下划线，长度<=128"""
        if not isinstance(site, str):
            return False
        if len(site) == 0 or len(site) > 128:
            return False
        return re.match(r'^[A-Za-z0-9._-]+$', site) is not None

    def _safe_read_json(self, path):
        """安全读取JSON文件，失败返回None"""
        try:
            if not os.path.exists(path):
                return None
            body = public.readFile(path)
            if not body:
                return None
            return json.loads(body)
        except Exception:
            return None

    def _ensure_metrics(self, data):
        """规范化指标字典，缺失字段按0处理，类型转为int"""
        keys = ['traffic', 'requests', 'ip', 'uv', 'pv']
        result = {}
        for k in keys:
            try:
                v = int((data or {}).get(k, 0)) if isinstance(data, dict) else 0
            except Exception:
                v = 0
            result[k] = v
        return result

    def _humanize_bytes(self, n):
        """按1024换算返回人类可读格式"""
        try:
            n = int(n)
        except Exception:
            n = 0
        units = ['B', 'KB', 'MB', 'GB', 'TB']
        size = float(n)
        idx = 0
        while size >= 1024 and idx < len(units) - 1:
            size /= 1024.0
            idx += 1
        # 保留两位小数
        if idx == 0:
            return f"{int(size)} {units[idx]}"
        return f"{round(size, 2)} {units[idx]}"

    def _format_daily_stat(self, metrics, include_compare=None):
        """将指标格式化为返回结构，include_compare用于today对比昨日"""
        m = self._ensure_metrics(metrics or {})
        formatted = {
            'traffic_bytes': m['traffic'],
            'traffic_human': self._humanize_bytes(m['traffic']),
            'req_count': m['requests'],
            'ip_count': m['ip'],
            'uv': m['uv'],
            'pv': m['pv']
        }
        if include_compare is not None:
            formatted['compare_vs_yesterday'] = include_compare
        return formatted

    def _compute_compare(self, today_metrics, yesterday_metrics):
        """计算与昨日的对比，返回各指标的abs/pct/trend，pct保留两位小数"""
        t = self._ensure_metrics(today_metrics or {})
        y = self._ensure_metrics(yesterday_metrics or {})
        result = {}
        for src_k, out_k in [('traffic','traffic'), ('requests','req_count'), ('ip','ip_count'), ('uv','uv'), ('pv','pv')]:
            abs_change = t[src_k] - y[src_k]
            pct = 0.0
            if y[src_k] > 0:
                pct = round((abs_change / y[src_k]) * 100.0, 2)
            else:
                # 昨日为0，无法计算百分比，按规则返回0
                pct = 0.0
            trend = 'flat'
            if abs_change > 0:
                trend = 'up'
            elif abs_change < 0:
                trend = 'down'
            result[out_k] = {'abs': abs_change, 'pct': pct, 'trend': trend}
        return result

    def _monitor_enabled(self):
        """检测是否启用监控报表数据源。存在 /www/server/panel/plugin/monitor 且配置中的 data_save_path 可用时返回 True"""
        try:
            if not os.path.exists('/www/server/panel/plugin/monitor'):
                return False
            db_path = self._get_monitor_db_path()
            return bool(db_path and os.path.isdir(db_path))
        except Exception as e:
            return False

    def _get_monitor_db_path(self):
        """读取监控报表配置，获取 data_save_path。结果缓存到实例属性以减少IO"""
        try:
            if hasattr(self, '_monitor_db_path') and self._monitor_db_path:
                return self._monitor_db_path
            conf_file = '/www/server/panel/plugin/monitor/monitor_data/config/config.json'
            conf_data = None
            try:
                conf_str = public.readFile(conf_file)
                conf_data = json.loads(conf_str) if conf_str else None
            except Exception:
                conf_data = None
            db_path = None
            if isinstance(conf_data, dict):
                db_path = conf_data.get('data_save_path')
            self._monitor_db_path = db_path
            return db_path
        except Exception:
            return None

    def _list_sites_monitor(self):
        """从监控报表数据目录枚举站点子目录（仅合法站点名且存在 request_total.db）"""
        sites = []
        try:
            base = self._get_monitor_db_path()
            if not base or not os.path.isdir(base):
                return sites
            for name in os.listdir(base):
                full = os.path.join(base, name)
                db_file = os.path.join(full, 'request_total.db')
                if os.path.isdir(full) and self._validate_site_name(name) and os.path.isfile(db_file):
                    sites.append(name)
        except Exception:
            pass
        return sites

    def _read_site_day_from_monitor(self, site, date_str):
        """从监控报表 request_total.db 读取单站某日指标，异常或缺失返回0集"""
        result = {'traffic': 0, 'requests': 0, 'ip': 0, 'uv': 0, 'pv': 0}
        try:
            base = self._get_monitor_db_path()
            if not base:
                return result
            db_file = os.path.join(base, site, 'request_total.db')
            if not os.path.isfile(db_file):
                return result
            # 日期转换为YYYYMMDD
            ymd = date_str.replace('-', '')
            ts = db.Sql()
            ts._Sql__DB_FILE = db_file
            fields = 'SUM(sent_bytes) as traffic, SUM(uv_number) as uv, SUM(ip_number) as ip, SUM(pv_number) as pv, SUM(request) as requests'
            row = ts.table('request_total').where("date=?", (ymd,)).field(fields).find()
            ts.close()
            if isinstance(row, dict) and row:
                for k in result.keys():
                    try:
                        result[k] = int(row.get(k, 0) or 0)
                    except Exception:
                        result[k] = 0
        except Exception:
            pass
        return result

    def _ensure_summary_dir(self):
        """确保SUMMARY_DIR存在"""
        try:
            if not os.path.isdir(self.SUMMARY_DIR):
                os.makedirs(self.SUMMARY_DIR, exist_ok=True)
        except Exception:
            pass

    def _safe_write_json_atomic(self, path, data):
        """原子写入JSON：先写临时文件，再替换为目标文件"""
        try:
             dir_name = os.path.dirname(path)
             try:
                 os.makedirs(dir_name, exist_ok=True)
             except Exception:
                 pass
             base_name = os.path.basename(path)
             tmp_path = os.path.join(dir_name, '.' + base_name + '.tmp')
             with open(tmp_path, 'w', encoding='utf-8') as f:
                 json.dump(data, f, ensure_ascii=False)
             os.replace(tmp_path, path)
             return True
        except Exception:
            try:
                # 回滚临时文件
                if 'tmp_path' in locals() and os.path.exists(tmp_path):
                    os.remove(tmp_path)
            except Exception:
                pass
            return False

    def _list_sites(self):
        """枚举站点列表：优先使用监控报表数据源，否则遍历TOTAL_DIR下的目录"""
        try:
            if self._monitor_enabled():
                return self._list_sites_monitor()
        except Exception:
            # 监控数据源异常时回退旧方式
            pass
        sites = []
        base = self.TOTAL_DIR
        try:
            if not os.path.exists(base):
                return sites
            for name in os.listdir(base):
                full = os.path.join(base, name)
                if os.path.isdir(full) and self._validate_site_name(name):
                    sites.append(name)
        except Exception:
            pass
        return sites

    def _site_day_path(self, site, date_str):
        """拼接单站点某日JSON路径：/total/{site}/{YYYY-MM-DD}.json"""
        return os.path.join(self.TOTAL_DIR, site, f"{date_str}.json")

    def _load_summary_for_date(self, date_str):
        """读取全站汇总持久文件 /summary/{YYYY-MM-DD}.json，返回指标或None"""
        path = os.path.join(self.SUMMARY_DIR, f"{date_str}.json")
        data = self._safe_read_json(path)
        if data is None:
            return None
        return self._ensure_metrics(data)

    def _aggregate_all_sites_for_date(self, date_str):
        """聚合全站在某日的指标（遍历站点逐一读取），缺失文件按0处理"""
        total = {'traffic': 0, 'requests': 0, 'ip': 0, 'uv': 0, 'pv': 0}
        for site in self._list_sites():
            m = self._aggregate_site_for_date(site, date_str)
            for k in total.keys():
                total[k] += m.get(k, 0)
        return total

    def _aggregate_site_for_date(self, site, date_str):
        """读取单站点某日指标（优先监控报表DB，失败回退旧文件），缺失或异常返回全部0"""
        # 优先从监控报表数据源读取
        try:
            if self._monitor_enabled():
                return self._ensure_metrics(self._read_site_day_from_monitor(site, date_str))
        except:
            # 数据源异常时回退旧方式
            pass
        # 旧文件方式
        path = self._site_day_path(site, date_str)
        data = self._safe_read_json(path)
        m = self._ensure_metrics(data or {})
        return m

    def _get_all_sites_total_for_date(self, date_str):
        """优先读取summary；不存在则回退聚合原始站点文件"""
        summary = self._load_summary_for_date(date_str)
        if summary is not None:
            return summary
        return self._aggregate_all_sites_for_date(date_str)

    def _get_site_total_for_date(self, site, date_str):
        """读取单站某日指标（直接读取原始文件）"""
        return self._aggregate_site_for_date(site, date_str)

    def _build_trend_7days_all(self):
        """构建全站近7天趋势points数组（历史6天缺失则计算并写入缓存，今日实时不写缓存）"""
        points = []
        today = datetime.now().strftime('%Y-%m-%d')
        for d in self._get_7day_dates():
            if d == today:
                # 今日实时统计，跳过summary缓存
                m = self._aggregate_all_sites_for_date(d)
            else:
                # 历史天优先读取summary，缺失则实时计算并写入缓存
                summary = self._load_summary_for_date(d)
                if summary is None:
                    m = self._aggregate_all_sites_for_date(d)
                    # 写入SUMMARY_DIR/{YYYY-MM-DD}.json
                    try:
                        self._safe_write_json_atomic(os.path.join(self.SUMMARY_DIR, f"{d}.json"), self._ensure_metrics(m))
                    except Exception:
                        pass
                else:
                    m = summary
            points.append({
                'date': d,
                'traffic_bytes': m['traffic'],
                'traffic_human': self._humanize_bytes(m['traffic']),
                'req_count': m['requests'],
                'ip_count': m['ip'],
                'uv': m['uv'],
                'pv': m['pv']
            })
        return points

    def _build_trend_7days_site(self, site):
        """构建单站近7天趋势points数组（历史天缺失则计算并写入 /total/{site}/{YYYY-MM-DD}.json；今日实时不写缓存）"""
        points = []
        today = datetime.now().strftime('%Y-%m-%d')
        for d in self._get_7day_dates():
            if d == today:
                m = self._aggregate_site_for_date(site, d)
            else:
                path = self._site_day_path(site, d)
                data = self._safe_read_json(path)
                if data is None:
                    m = self._aggregate_site_for_date(site, d)
                    try:
                        self._safe_write_json_atomic(path, self._ensure_metrics(m))
                    except Exception:
                        pass
                else:
                    m = self._ensure_metrics(data)
            points.append({
                'date': d,
                'traffic_bytes': m['traffic'],
                'traffic_human': self._humanize_bytes(m['traffic']),
                'req_count': m['requests'],
                'ip_count': m['ip'],
                'uv': m['uv'],
                'pv': m['pv']
            })
        return points

    def _get_top_sites_for_date(self, date_str, metric='pv', order='asc', limit=5):
        """计算指定日期的站点当天排行"""
        if metric not in ['traffic', 'req_count', 'ip_count', 'uv', 'pv']:
            metric = 'pv'
        if order not in ['asc', 'desc']:
            order = 'asc'
        result = []
        for site in self._list_sites():
            m = self._aggregate_site_for_date(site, date_str)
            result.append({
                'site': site,
                'traffic_bytes': m['traffic'],
                'traffic_human': self._humanize_bytes(m['traffic']),
                'req_count': m['requests'],
                'ip_count': m['ip'],
                'uv': m['uv'],
                'pv': m['pv']
            })
        # 映射排序字段
        metric_alias = {'traffic': 'traffic_bytes', 'req_count': 'req_count', 'ip_count': 'ip_count', 'uv': 'uv', 'pv': 'pv'}
        sort_key = metric_alias.get(metric, 'pv')
        reverse = (order == 'desc')
        result.sort(key=lambda x: int(x.get(sort_key, 0)), reverse=reverse)
        if isinstance(limit, int):
            if limit <= 0:
                limit = 5
            if limit > 50:
                limit = 50
        else:
            limit = 5
        return result[:limit]

    def _get_top_sites_last_7_days(self, metric='pv', order='asc', limit=5):
        """计算近7天累计的站点排行（固定metric=pv，order=asc）"""
        if metric not in ['traffic', 'req_count', 'ip_count', 'uv', 'pv']:
            metric = 'pv'
        if order not in ['asc', 'desc']:
            order = 'asc'
        dates = self._get_7day_dates()
        result = []
        for site in self._list_sites():
            agg = {'traffic': 0, 'requests': 0, 'ip': 0, 'uv': 0, 'pv': 0}
            for d in dates:
                m = self._aggregate_site_for_date(site, d)
                for k in agg.keys():
                    agg[k] += m.get(k, 0)
            result.append({
                'site': site,
                'traffic_bytes': agg['traffic'],
                'traffic_human': self._humanize_bytes(agg['traffic']),
                'req_count': agg['requests'],
                'ip_count': agg['ip'],
                'uv': agg['uv'],
                'pv': agg['pv']
            })
        # 排序字段映射
        metric_alias = {'traffic': 'traffic_bytes', 'req_count': 'req_count', 'ip_count': 'ip_count', 'uv': 'uv', 'pv': 'pv'}
        sort_key = metric_alias.get(metric, 'pv')
        reverse = (order == 'desc')
        result.sort(key=lambda x: int(x.get(sort_key, 0)), reverse=reverse)
        if isinstance(limit, int):
            if limit <= 0:
                limit = 5
            if limit > 50:
                limit = 50
        else:
            limit = 5
        return result[:limit]

    def _get_rec_status(self):
        try:
            u = public.get_user_info()
            if not isinstance(u, dict):
                return False
            serverid = u.get('serverid')
            access_key = u.get('access_key')
            uid = u.get('uid')
            if not serverid or not access_key or uid is None:
                return False
            payload = {
                'serverid': serverid,
                'access_key': access_key,
                'uid': uid,
                'activity_id': 44
            }
            url = 'https://www.yakpanel.com/newapi/activity/panelapi/get_free_activity_info'
            res = public.httpPost(url, payload)
            if not res:
                return False
            try:
                obj = json.loads(res)
            except Exception:
                return False
            data = obj.get('data')
            if isinstance(data, dict):
                s = data.get('status')
                detail = data.get('detail')
                buy_status = None
                if isinstance(detail, list) and len(detail) > 0:
                    buy_status = detail[0].get('buy_status')
                elif isinstance(detail, dict):
                    buy_status = detail.get('buy_status')
                return (s == 1 or str(s) == '1') and (buy_status == 1 or str(buy_status) == '1')
            if isinstance(data, list) and len(data) > 0:
                item = data[0]
                s = item.get('status')
                detail = item.get('detail')
                buy_status = None
                if isinstance(detail, list) and len(detail) > 0:
                    buy_status = detail[0].get('buy_status')
                elif isinstance(detail, dict):
                    buy_status = detail.get('buy_status')
                return (s == 1 or str(s) == '1') and (buy_status == 1 or str(buy_status) == '1')
            return False
        except Exception:
            return False

    def _get_rec_status_detail(self):
        try:
            u = public.get_user_info()
            if not isinstance(u, dict):
                return False, None
            serverid = u.get('serverid')
            access_key = u.get('access_key')
            uid = u.get('uid')
            if not serverid or not access_key or uid is None:
                return False, None
            payload = {
                'serverid': serverid,
                'access_key': access_key,
                'uid': uid,
                'activity_id': 44
            }
            url = 'https://www.yakpanel.com/newapi/activity/panelapi/get_free_activity_info'
            res = public.httpPost(url, payload)
            if not res:
                return False, None
            try:
                obj = json.loads(res)
            except Exception:
                return False, None
            data = obj.get('data')
            detail_id = None
            if isinstance(data, dict):
                s = data.get('status')
                detail = data.get('detail')
                buy_status = None
                if isinstance(detail, list) and len(detail) > 0:
                    buy_status = detail[0].get('buy_status')
                    detail_id = detail[0].get('id')
                elif isinstance(detail, dict):
                    buy_status = detail.get('buy_status')
                    detail_id = detail.get('id')
                return (s == 1 or str(s) == '1') and (buy_status == 1 or str(buy_status) == '1'), detail_id
            if isinstance(data, list) and len(data) > 0:
                item = data[0]
                s = item.get('status')
                detail = item.get('detail')
                buy_status = None
                if isinstance(detail, list) and len(detail) > 0:
                    buy_status = detail[0].get('buy_status')
                    detail_id = detail[0].get('id')
                elif isinstance(detail, dict):
                    buy_status = detail.get('buy_status')
                    detail_id = detail.get('id')
                return (s == 1 or str(s) == '1') and (buy_status == 1 or str(buy_status) == '1'), detail_id
            return False, None
        except Exception:
            return False, None

    def _check_config(self):
        """
        @description 检查config配置是否需要更新,修复统计数量不显示问题
        @return None
        """
        header_file = "{}/data/table_header_conf.json".format(public.get_panel_path())
        try:
            if os.path.exists(header_file):
                raw = public.readFile(header_file)
                file_data = json.loads(raw)
                if isinstance(file_data, dict):
                    updated = False
                    val = file_data.get("phpTableColumn", '')
                    if val:
                        try:
                            cols = json.loads(val) or []
                            has_day = False
                            for c in cols:
                                if c.get("label") == "daily flow":
                                    has_day = True
                                    if c.get("isCustom") is not True:
                                        c["isCustom"] = True
                                    if c.get("isLtd") is not True:
                                        c["isLtd"] = True
                                    break
                            if not has_day:
                                cols.append({"label": "daily flow", "width": 80, "isCustom": True, "isLtd": True})
                            file_data["phpTableColumn"] = json.dumps(cols)
                            updated = True
                        except Exception:
                            pass
                    if updated:
                        public.writeFile(header_file, json.dumps(file_data))
        except Exception:
            pass