gaoluyang
2 天以前 e71d71bf43c2307c89d3592adf08c1b32fcd0907
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * 能耗台账 10 月补充生成器(模板快照 -> 补录 SQL)
 *
 * 目标库:36.213.150.237:10000/mom-hm
 * 依据:9 月已有的每日台账模板(8 区域 × 水/电/汽)
 *
 * 规则(已与用户确认):
 *  - 范围:2026-10-01 ~ 2026-10-14(不含 15 日)
 *  - 沿用 9 月模板:工作日 21 条/天(水/电/汽),节假日 16 条/天(仅水/电,无蒸汽)
 *  - 节假日:10/1–10/7(国庆)+ 10/11(周日),用量约为工作日的 30%
 *  - 保留库里已有的 10/8、10/9、10/12、10/14 四条零散记录(不删除、不覆盖)
 *  - 单价沿用:水 5.2 / 电 0.83 / 汽 248(元/能源单位)
 *  - 金额 = 用量 × 单价,四舍五入到分
 */
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
 
const DIR = path.dirname(fileURLToPath(import.meta.url));
const BIZ = 'energy-record-fill-20261015';
 
const UNIT = { WATER: 5.2, ELECTRICITY: 0.83, STEAM: 248 };
 
// 日报模板:[区域, 产品类别, 用途, 能源类型, 工作日基准用量, 录入人, 创建人]
const TPL = [
  ['燕窝生产区', '燕窝', '蒸煮提取', 'WATER', 12.5, '李万娟', '244'],
  ['燕窝生产区', '燕窝', '蒸煮提取', 'ELECTRICITY', 520, '李万娟', '244'],
  ['燕窝生产区', '燕窝', '蒸煮提取', 'STEAM', 1.85, '李万娟', '244'],
  ['原浆生产区', '滋补品原浆', '蒸煮提取', 'WATER', 17.5, '李万娟', '244'],
  ['原浆生产区', '滋补品原浆', '蒸煮提取', 'ELECTRICITY', 640, '李万娟', '244'],
  ['原浆生产区', '滋补品原浆', '蒸煮提取', 'STEAM', 2.45, '李万娟', '244'],
  ['清洗区', '燕窝', '原料清洗', 'WATER', 22.0, '李万娟', '244'],
  ['清洗区', '燕窝', '设备清洗 / CIP', 'ELECTRICITY', 145, '李万娟', '244'],
  ['清洗区', '燕窝', '设备清洗 / CIP', 'STEAM', 0.72, '李万娟', '244'],
  ['灭菌区', '燕窝', '灭菌', 'WATER', 6.2, '李万娟', '244'],
  ['灭菌区', '燕窝', '灭菌', 'ELECTRICITY', 300, '李万娟', '244'],
  ['灭菌区', '燕窝', '灭菌', 'STEAM', 1.22, '李万娟', '244'],
  ['灌装包装区', '燕窝', '灌装包装', 'WATER', 5.0, '李万娟', '244'],
  ['灌装包装区', '燕窝', '灌装包装', 'ELECTRICITY', 420, '李万娟', '244'],
  ['灌装包装区', '燕窝', '灌装包装', 'STEAM', 0.58, '李万娟', '244'],
  ['动力站', '公用能耗', '制冷', 'WATER', 27.5, '赵磊', '243'],
  ['动力站', '公用能耗', '制冷', 'ELECTRICITY', 1950, '赵磊', '243'],
  ['仓储区', '公用能耗', '日常用能', 'WATER', 1.2, '赵磊', '243'],
  ['仓储区', '公用能耗', '制冷', 'ELECTRICITY', 380, '赵磊', '243'],
  ['办公生活区', '公用能耗', '日常用能', 'WATER', 9.5, '赵磊', '243'],
  ['办公生活区', '公用能耗', '日常用能', 'ELECTRICITY', 300, '赵磊', '243'],
];
 
const HOLIDAYS = new Set([
  '2026-10-01', '2026-10-02', '2026-10-03', '2026-10-04',
  '2026-10-05', '2026-10-06', '2026-10-07', '2026-10-11',
]);
 
const DATES = [];
for (let d = 1; d <= 14; d++) DATES.push(`2026-10-${String(d).padStart(2, '0')}`);
 
const HOLIDAY_SCALE = 0.30;
 
// 确定性伪随机(mulberry32),按日期+行序播种,保证重跑结果一致
function rng(seed) {
  let a = seed >>> 0;
  return () => {
    a |= 0; a = (a + 0x6D2B79F5) | 0;
    let t = Math.imul(a ^ (a >>> 15), 1 | a);
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
  };
}
const dayNum = (s) => Number(s.slice(8, 10));
const round = (v, n) => Number(v.toFixed(n));
 
const q = (v) => `'${String(v).replaceAll("'", "''")}'`;
const rows = [];
 
for (const date of DATES) {
  const holiday = HOLIDAYS.has(date);
  const tpl = holiday ? TPL.filter((r) => r[3] !== 'STEAM') : TPL;
  tpl.forEach(([area, category, purpose, type, base, recorder, creator], idx) => {
    const rand = rng(dayNum(date) * 1000 + idx)();
    const jitter = 0.85 + 0.30 * rand;              // ±约15%
    const scale = holiday ? HOLIDAY_SCALE : 1;
    const quantity = round(base * scale * jitter, 3);
    const unitPrice = UNIT[type];
    const amount = round(quantity * unitPrice, 2);
    rows.push({
      date, type, area, category, purpose, quantity, unitPrice, amount, recorder, creator, holiday,
    });
  });
}
 
const out = [];
out.push(`-- 能耗台账 10 月补充 | 目标库 mom-hm | ${BIZ}`);
out.push('-- 范围 2026-10-01 ~ 2026-10-14;节假日(10/1-10/7、10/11)仅水/电、用量约 3 成。');
out.push('-- 回滚:DELETE FROM mes_energy_record WHERE record_date BETWEEN \'2026-10-01\' AND \'2026-10-14\';');
out.push('--       (注意:会同时删掉 10/8、10/9、10/12、10/14 原有的 4 条记录,需事先备份。)');
out.push('START TRANSACTION;');
out.push(`\n-- mes_energy_record : ${rows.length} 行`);
const vals = rows.map((r) =>
  `(${q(r.date)},${q(r.type)},${q(r.area)},${q(r.category)},${q(r.purpose)},${r.quantity.toFixed(3)},${r.unitPrice.toFixed(4)},${r.amount.toFixed(2)},${q(r.recorder)},'',${q(r.date + ' 17:30:00')},${q(r.creator)},${q(r.date + ' 17:30:00')},b'0')`,
);
for (let i = 0; i < vals.length; i += 200) {
  out.push(
    `INSERT INTO mes_energy_record (record_date,energy_type,usage_area,product_category,usage_purpose,quantity,unit_price,amount,recorder,remark,create_time,creator,update_time,deleted) VALUES\n${vals.slice(i, i + 200).join(',\n')};`,
  );
}
out.push('\nCOMMIT;');
 
fs.writeFileSync(path.join(DIR, 'energy-oct.sql'), out.join('\n'), 'utf8');
fs.writeFileSync(path.join(DIR, 'energy-oct-plan.json'), JSON.stringify({ count: rows.length, rows }, null, 1), 'utf8');
 
const byDate = {};
for (const r of rows) byDate[r.date] = (byDate[r.date] ?? 0) + 1;
console.log('total rows:', rows.length);
console.log(byDate);