wangrong
2026-09-26 22c5f37a587bfb3363534fed05647faf5f93c8fe
修改首页显示和统计任务
4个文件已修改
68 ■■■■■ 已修改文件
src/config/config.local.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/config.prod.ts 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/service/screen.ts 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/modules/report/service/taskservicesite.ts 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/config/config.local.ts
@@ -15,10 +15,10 @@
      default: {
        type: 'mysql',
        host: '127.0.0.1',
        port: 8806,
        port: 3306,
        username: 'womp',
        password: 'Sening@2023',
        database: 'womp-shop',
        database: 'womp_shop',
        // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
        synchronize: false,
        // 打印日志
src/config/config.prod.ts
@@ -27,10 +27,10 @@
      default: {
        type: 'mysql',
        host: '127.0.0.1',
        port: 8806,
        port: 3306,
        username: 'womp',
        password: 'Sening@2023',
        database: 'womp-shop',
        database: 'womp_shop',
        // 自动建表 注意:线上部署的时候不要使用,有可能导致数据丢失
        synchronize: false,
        // 打印日志
src/modules/report/service/screen.ts
@@ -371,17 +371,19 @@
    return (await qb.getRawOne()) || {};
  }
  /** 名称取全部种类;codes 仍只含启用项,供 KPI/趋势沿用原口径。分类图不再用 codes 过滤。 */
  private async enabledWaste() {
    const list = await this.wasteTypeRepo.find({
      where: { status: 1 },
      select: ['code', 'name'],
      select: ['code', 'name', 'status'],
    });
    const names = new Map<string, string>();
    const codes = new Set<string>();
    list.forEach(e => {
      const code = String(e.code).toUpperCase();
      codes.add(code);
      names.set(code, e.name);
      if (Number(e.status) === 1) {
        codes.add(code);
      }
    });
    return { names, codes };
  }
@@ -412,6 +414,7 @@
    return cols.map(col => `IFNULL(a.${col},0)`).join(' + ');
  }
  /** 首页分类全量返回,不按种类启用状态裁剪;0 值由前端图表隐藏。 */
  private buildWasteMix(row: any, waste: { names: Map<string, string>; codes: Set<string> }) {
    const items = [
      { code: 'SW62', key: 'Sw62' },
@@ -419,7 +422,7 @@
      { code: 'SW63', key: 'Sw63' },
      { code: 'SW61', key: 'Sw61' },
      { code: 'SW60', key: 'Sw60' },
    ].filter(e => waste.codes.has(e.code));
    ];
    return items.map(e => ({
      name: waste.names.get(e.code) || this.defaultWasteName(e.code),
      code: e.code,
@@ -442,11 +445,11 @@
    );
  }
  /** 可回收 7 子类全量返回;碳值保留符号(减排可为负),图表侧取绝对值展示。 */
  private buildRecycleItems(row: any, waste: { names: Map<string, string>; codes: Set<string> }) {
    const fallback = ['废纸', '塑料', '金属', '织物', '玻璃', '电子产品', '其他可回收'];
    return ['001', '002', '003', '004', '005', '006', '007']
      .map((idx, i) => ({ idx, i, code: `SW62${idx}` }))
      .filter(e => waste.codes.has(e.code))
      .map(e => ({
        name: waste.names.get(e.code) || fallback[e.i],
        code: e.code,
src/modules/report/service/taskservicesite.ts
@@ -55,18 +55,33 @@
        // 3. 内存聚合核心:定义支持动态键名累加的对象字典
        const siteAggMap = new Map<string, any>();
        // 映射表:将明细里的 garbageType 映射到你 Entity 里的字段后缀
        // 源数据 garbageType 为大写 SW62001…SW62007,没有单独的 SW62 流水。
        // 先按 7 个子类分别累计,再把重量/碳排/销售额汇总进 SW62。
        const SW62_SUB_CODES = [
            'SW62001', 'SW62002', 'SW62003', 'SW62004',
            'SW62005', 'SW62006', 'SW62007',
        ];
        const SW62_SUB_SUFFIXES = SW62_SUB_CODES.map(code => `Sw${code.slice(2)}`);
        const typeSuffixes = [
            'Sw60', 'Sw61', 'Sw62', 'Sw63', 'Sw64',
            ...SW62_SUB_SUFFIXES,
        ];
        const typeKeyMap: Record<string, string> = {
            'SW60': 'Sw60', 'SW61': 'Sw61', 'SW62': 'Sw62', 'SW63': 'Sw63', 'SW64': 'Sw64',
            'Sw62001': 'Sw62001', 'Sw62002': 'Sw62002', 'Sw62003': 'Sw62003',
            'Sw62004': 'Sw62004', 'Sw62005': 'Sw62005', 'Sw62006': 'Sw62006', 'Sw62007': 'Sw62007'
            SW60: 'Sw60',
            SW61: 'Sw61',
            SW63: 'Sw63',
            SW64: 'Sw64',
        };
        for (const code of SW62_SUB_CODES) {
            const suffix = `Sw${code.slice(2)}`;
            typeKeyMap[code] = suffix;
            typeKeyMap[suffix] = suffix;
        }
        for (const row of rawDetails) {
            const siteId = row.siteId;
            const weight = Number(row.weight) || 0;
            const gType = row.garbageType || 'SW64';
            const gType = String(row.garbageType || 'SW64').trim().toUpperCase();
            const fieldSuffix = typeKeyMap[gType];
            // 初始化该站点的聚合结构
@@ -79,7 +94,7 @@
                    weightTotal: 0, timeTotal: 0, carbonTotal: 0
                };
                // 动态初始化宽表里各子类型的 0 值
                Object.values(typeKeyMap).forEach(suffix => {
                typeSuffixes.forEach(suffix => {
                    initData[`weight${suffix}`] = 0;
                    initData[`time${suffix}`] = 0;
                    initData[`carbon${suffix}`] = 0;
@@ -119,6 +134,24 @@
            }
        }
        // SW62 没有独立流水,用 7 个子类重量/碳排/销售额加总写入父类字段
        for (const agg of siteAggMap.values()) {
            let weightSw62 = 0;
            let carbonSw62 = 0;
            let salesSw62 = 0;
            let timeSw62 = 0;
            for (const suffix of SW62_SUB_SUFFIXES) {
                weightSw62 += Number(agg[`weight${suffix}`]) || 0;
                carbonSw62 += Number(agg[`carbon${suffix}`]) || 0;
                salesSw62 += Number(agg[`estimatedSales${suffix}`]) || 0;
                timeSw62 = Math.max(timeSw62, Number(agg[`time${suffix}`]) || 0);
            }
            agg.weightSw62 = weightSw62;
            agg.carbonSw62 = carbonSw62;
            agg.estimatedSalesSw62 = salesSw62;
            agg.timeSw62 = timeSw62;
        }
        // 4. 高性能写入:转换为数组,利用批量插入 + ON DUPLICATE KEY UPDATE 提交给 MySQL
        const finalRecords = Array.from(siteAggMap.values()).map(item => {
            // 浮点数防精度丢失处理,保留两位小数