-- ============================================================================= -- 酒厂账单一次性修复(v4.0.6) -- 库:生产 dukang_prod(也可在已同步的本地 dukang_haoke 先演练) -- -- 规则(与代码一致): -- 起点 WINERY_BILL_EPOCH_YMD = 2026-08-01 -- 周期 WINERY_SETTLEMENT_PERIOD_DAYS = 3 -- 出账日 8/4、8/7、8/10 … ;纳入窗口 [出账日-3, 出账日) -- 订单:status=COMPLETED AND pay_status=PAID -- delivery_type IN ('LOCAL','CROSS_CITY','ON_SITE_PICKUP') -- 账期归属:completed_at(缺则 paid_at)的北京日历日 -- 应付 = ROUND(实付合计 × 0.3, 2);出账日无单仍留账单(无需打款) -- 已打款账单不改明细 -- 起点之前已入账的订单(如 7/28 同城挂在 7/31)保持原账单,不迁到 8/4 -- -- 本脚本会: -- 1) 补现场提货等缺 completed_at 的已完成单 -- 2) 按周期补齐出账日账单(含中间应付为 0 的空账) -- 3) 把未入账 / 挂错期(如 8/29)的同城、跨城、现场提货订单迁入对应出账日 -- 4) 按明细重算未打款表头 -- 5) 删除未打款、无明细、且非周期日的空账单(7/29、8/1、8/29 等) -- -- 建议:先部署含「每 3 天出一期」的 API,再跑本脚本。 -- 若必须先修数据:跑完后尽快发版,避免旧「每日只收 D-3 当天」任务回刷 8/28。 -- -- 用法(生产): -- mysql -u... -p dukang_prod < fix-winery-bills-v406.sql -- ============================================================================= START TRANSACTION; -- 1) 现场提货等:支付即完成但未写 completed_at UPDATE user_order SET completed_at = paid_at WHERE status = 'COMPLETED' AND pay_status = 'PAID' AND completed_at IS NULL AND paid_at IS NOT NULL; -- 2) 补齐周期出账日(8/4 起至「今天北京日」能落到的最后一张) INSERT INTO winery_bill ( bill_no, bill_date, order_count, order_amount, winery_rate, winery_amount, status, created_at ) WITH RECURSIVE issue_days AS ( SELECT DATE('2026-08-04') AS issue_ymd UNION ALL SELECT DATE_ADD(issue_ymd, INTERVAL 3 DAY) FROM issue_days WHERE DATE_ADD(issue_ymd, INTERVAL 3 DAY) <= DATE(DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR)) ) SELECT CONCAT('WBFIX', DATE_FORMAT(d.issue_ymd, '%Y%m%d')) AS bill_no, TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR)) AS bill_date, 0, 0.00, 0.3000, 0.00, 'UNPAID', TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR)) FROM issue_days d WHERE NOT EXISTS ( SELECT 1 FROM winery_bill b WHERE b.bill_date = TIMESTAMP(DATE_ADD(d.issue_ymd, INTERVAL -8 HOUR)) ); -- 3) 起点日及之后:从未打款账单上拿掉挂错期的明细(已打款账单因 status 过滤不会动) DELETE i FROM winery_bill_item i INNER JOIN winery_bill b ON b.id = i.winery_bill_id INNER JOIN user_order o ON o.id = i.order_id INNER JOIN winery_bill target ON target.bill_date = TIMESTAMP(DATE_ADD( DATE_ADD( DATE('2026-08-01'), INTERVAL (FLOOR(DATEDIFF( DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')), DATE('2026-08-01') ) / 3) + 1) * 3 DAY ), INTERVAL -8 HOUR )) WHERE b.status = 'UNPAID' AND b.id <> target.id AND o.status = 'COMPLETED' AND o.pay_status = 'PAID' AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP') AND COALESCE(o.completed_at, o.paid_at) IS NOT NULL AND DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) >= '2026-08-01'; -- 4) 把未入账的同城/跨城/现场提货订单写入对应周期账单 INSERT INTO winery_bill_item ( winery_bill_id, order_id, order_no, delivery_type, pay_amount, winery_amount, paid_at, created_at ) SELECT target.id, o.id, o.order_no, o.delivery_type, o.pay_amount, ROUND(o.pay_amount * 0.3, 2), COALESCE(o.paid_at, o.completed_at), UTC_TIMESTAMP(3) FROM user_order o INNER JOIN winery_bill target ON target.bill_date = TIMESTAMP(DATE_ADD( DATE_ADD( DATE('2026-08-01'), INTERVAL (FLOOR(DATEDIFF( DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')), DATE('2026-08-01') ) / 3) + 1) * 3 DAY ), INTERVAL -8 HOUR )) LEFT JOIN winery_bill_item existing ON existing.order_id = o.id WHERE o.status = 'COMPLETED' AND o.pay_status = 'PAID' AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP') AND COALESCE(o.completed_at, o.paid_at) IS NOT NULL AND DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) >= '2026-08-01' AND existing.id IS NULL; -- 5) 按明细重算所有未打款表头(应付为 0 的周期账单也保留) UPDATE winery_bill b LEFT JOIN ( SELECT winery_bill_id, COUNT(*) AS order_count, ROUND(SUM(pay_amount), 2) AS order_amount FROM winery_bill_item GROUP BY winery_bill_id ) x ON x.winery_bill_id = b.id SET b.order_count = IFNULL(x.order_count, 0), b.order_amount = IFNULL(x.order_amount, 0.00), b.winery_rate = 0.3000, b.winery_amount = ROUND(IFNULL(x.order_amount, 0) * 0.3, 2) WHERE b.status = 'UNPAID'; -- 6) 清掉误生成的非周期日空账单(有明细的如 7/31 保留;8/1 虽 MOD=0 但未满 3 天,仍删) DELETE b FROM winery_bill b LEFT JOIN winery_bill_item i ON i.winery_bill_id = b.id WHERE b.status = 'UNPAID' AND i.id IS NULL AND NOT ( DATEDIFF(DATE(CONVERT_TZ(b.bill_date, '+00:00', '+08:00')), DATE('2026-08-01')) >= 3 AND MOD(DATEDIFF(DATE(CONVERT_TZ(b.bill_date, '+00:00', '+08:00')), DATE('2026-08-01')), 3) = 0 ); COMMIT; -- ----------------------------------------------------------------------------- -- 核对(不改数据) -- ----------------------------------------------------------------------------- SELECT '=== 账单列表 ===' AS s; SELECT DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')) AS bill_ymd, bill_no, order_count, order_amount, winery_amount, status FROM winery_bill ORDER BY bill_date; SELECT '=== 明细按配送类型 ===' AS s; SELECT delivery_type, COUNT(*) AS cnt, ROUND(SUM(pay_amount), 2) AS pay_sum FROM winery_bill_item GROUP BY delivery_type; SELECT '=== 应入账但未入账(应为 0 行)===' AS s; SELECT o.id, o.order_no, o.delivery_type, o.pay_amount, DATE(CONVERT_TZ(COALESCE(o.completed_at, o.paid_at), '+00:00', '+08:00')) AS attr_ymd FROM user_order o LEFT JOIN winery_bill_item i ON i.order_id = o.id WHERE o.status = 'COMPLETED' AND o.pay_status = 'PAID' AND o.delivery_type IN ('LOCAL', 'CROSS_CITY', 'ON_SITE_PICKUP') AND i.id IS NULL; SELECT '=== 非周期日仍在的账单(仅允许起点前已入账的,如 7/31)===' AS s; SELECT DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')) AS bill_ymd, bill_no, order_count, order_amount, status FROM winery_bill WHERE NOT ( DATEDIFF(DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')), DATE('2026-08-01')) >= 3 AND MOD(DATEDIFF(DATE(CONVERT_TZ(bill_date, '+00:00', '+08:00')), DATE('2026-08-01')), 3) = 0 ) ORDER BY bill_date;