-- ============================================================================= -- 刷卡充电(SD)流程测试数据(实扣冻结锁定 + 授权停充 + 优惠结算) -- 创建日期: 2026-09-29 -- 目标环境: 测试库 Mycat(192.168.110.111:8066),逻辑库 other/account/order/service -- 配套脚本: doc/charging-link-test/run-sd-flow.ps1 -- 幂等: 可重复执行(先按 99xx 段清理再重建),与 VIN 流程数据(公司9901~9911/卡无)互不影响 -- -- 执行: -- mysql --host=192.168.110.111 --port=8066 --user=root --password=123456 \ -- --default-character-set=utf8mb4 --batch --force -e "source <本文件>" -- -- 场景矩阵(固定ID段 99xx;公司 9921~9933,卡 99201~99213,授权 9921~9933): -- S1 公司9921 共享(50) 卡99201(余额0) 授权 全天 site19 auto0 下发30 优惠 1=不优惠 -- S2 公司9922 分配 卡99202(余额20) 授权 全天 site19 auto0 下发30 优惠 1=不优惠 -- S3 公司9923 共享(50) 卡99203 授权 指定时段(now-2h~now+3min) auto1 下发30 -- S4 公司9924 共享(50) 卡99204 授权 全天 site_ids=25(枪在site19) -- S5 公司9925 共享(50) 卡99205 授权 指定时段 00:00~00:00(恒不命中) -- S5b 公司9931 共享(50) 卡99211 授权 指定时段 time_ranges=NULL(放行分支) -- S6a 公司9926 共享(0) 卡99206 授权 全天 下发30 -- S6b 公司9927 分配 卡99207(余额0) 授权 全天 下发30 -- S7 公司9928 共享(50/停用) 卡99208 授权 全天 下发30 -- S8a 公司9929 共享(50) 卡99209 授权 全天 site19 auto0 下发30 优惠 2=服务费折扣50% -- S8b 公司9930 共享(50) 卡99210 授权 全天 site19 auto0 下发30 优惠 3=计费模板9902 -- S9 公司9932 共享(50) 卡99212(禁用) 授权 全天 下发30 -> 卡鉴权失败 -- S10 公司9933 共享(50) 卡99213(充电中) 授权 全天 下发30 -> 同卡并发锁卡失败 -- -- 结算断言依赖(计费策略254,6段,按 start_time 排序;第N段读 {type}_chargeN): -- 段1 00:00-08:00 type4谷 0.3300/0.4000 段2 08:00-10:00 type3平 0.6813/0.4000 -- 段3 10:00-12:00 type2峰 1.0327/0.4000 段4/5/6 依次 平/峰/谷 同价 -- 上报 valley_charge1=1.0 + flat_charge2=1.0 + peak_charge3=1.0 -> -- S1/S2 电费 0.33+0.68+1.03=2.04 服务费 1.20 -> order_amount=3.24 -- S8a 服务费单价 0.40x50%=0.20x3=0.60 电费 2.04 -> order_amount=2.64 -- S8b 模板 谷0.20/0.30 平0.50/0.30 峰0.90/0.30 -> order_amount=2.50 -- 锁定口径(实扣冻结):共享模式锁企业账户 available_balance-额度 并记 locked_amount; -- 分配模式锁卡账户 balance-额度 并记 locked_amount。 -- ============================================================================= -- ============ 第0节 清理 ============ USE `charging_pile_order`; -- 注:Mycat 不支持带子查询的删除,订单快照/明细表(t_card_charging_order_accounting_strategy 等) -- 由 run-sd-flow.ps1 先按 card_id 查出订单ID、再用 IN (...) 显式删除。 DELETE FROM `t_card_charging_order` WHERE card_id BETWEEN 99201 AND 99213; USE `charging_pile_account`; DELETE FROM `t_company_allocate_record` WHERE company_id BETWEEN 9921 AND 9933; -- 卡余额流水(结算扣款 change_type=3 的唯一索引 uk_order_code 为幂等键):按卡ID段清理,保证重复执行后 -- 「扣款流水恰好1条」断言不受上一轮残留影响 DELETE FROM `t_charging_card_balance_record` WHERE card_id BETWEEN 99201 AND 99213; -- 测试卡:先清后建,保证重复执行幂等(卡余额/锁定标志/绑定订单号重置为新值) DELETE FROM `t_charging_card` WHERE id BETWEEN 99201 AND 99213; USE `charging_pile_other`; DELETE FROM `t_charge_authorization` WHERE company_id BETWEEN 9921 AND 9933; DELETE FROM `t_company_preferential` WHERE company_id BETWEEN 9921 AND 9933; DELETE FROM `t_group_company` WHERE id BETWEEN 9921 AND 9933; USE `charging_pile_service`; DELETE FROM `t_billing_template_detail` WHERE template_id = 9902; DELETE FROM `t_billing_template` WHERE id = 9902; -- ============ 第1节 测试公司(account_mode 1=共享 2=分配; status 1=启用 0=停用) ============ USE `charging_pile_other`; INSERT INTO `t_group_company` (`id`,`name`,`account_mode`,`available_balance`,`locked_amount`,`status`,`remark`,`del_flag`) VALUES (9921,'SD测试-共享无优惠', 1, 50.00, 0.00, 1,'S1', 0), (9922,'SD测试-分配无优惠', 2, 0.00, 0.00, 1,'S2', 0), (9923,'SD测试-授权到期停充', 1, 50.00, 0.00, 1,'S3', 0), (9924,'SD测试-站点超范围', 1, 50.00, 0.00, 1,'S4', 0), (9925,'SD测试-时段外', 1, 50.00, 0.00, 1,'S5', 0), (9926,'SD测试-共享余额0', 1, 0.00, 0.00, 1,'S6a',0), (9927,'SD测试-分配余额0', 2, 0.00, 0.00, 1,'S6b',0), (9928,'SD测试-公司停用', 1, 50.00, 0.00, 0,'S7', 0), (9929,'SD测试-比例优惠', 1, 50.00, 0.00, 1,'S8a',0), (9930,'SD测试-模板优惠', 1, 50.00, 0.00, 1,'S8b',0), (9931,'SD测试-空时段放行', 1, 50.00, 0.00, 1,'S5b',0), (9932,'SD测试-卡禁用', 1, 50.00, 0.00, 1,'S9', 0), (9933,'SD测试-同卡并发', 1, 50.00, 0.00, 1,'S10',0); -- ============ 第2节 测试卡(card_no 即设备上报 account 原文,区分大小写与前后空格) ============ -- status 1=启用 2=禁用;charging_flag 1=充电中锁定;balance 分配模式卡余额;locked_amount 锁定额度 USE `charging_pile_account`; INSERT INTO `t_charging_card` (`id`,`card_no`,`password`,`balance`,`locked_amount`,`company_id`,`fleet_id`,`status`,`charging_flag`,`charging_order_code`,`del_flag`) VALUES (99201,'99201',NULL, 0.00,0.00,9921,NULL,1,0,NULL,0), (99202,'99202',NULL,20.00,0.00,9922,NULL,1,0,NULL,0), (99203,'99203',NULL, 0.00,0.00,9923,NULL,1,0,NULL,0), (99204,'99204',NULL, 0.00,0.00,9924,NULL,1,0,NULL,0), (99205,'99205',NULL, 0.00,0.00,9925,NULL,1,0,NULL,0), (99206,'99206',NULL, 0.00,0.00,9926,NULL,1,0,NULL,0), (99207,'99207',NULL, 0.00,0.00,9927,NULL,1,0,NULL,0), (99208,'99208',NULL, 0.00,0.00,9928,NULL,1,0,NULL,0), (99209,'99209',NULL, 0.00,0.00,9929,NULL,1,0,NULL,0), (99210,'99210',NULL, 0.00,0.00,9930,NULL,1,0,NULL,0), (99211,'99211',NULL, 0.00,0.00,9931,NULL,1,0,NULL,0), (99212,'99212',NULL, 0.00,0.00,9932,NULL,2,0,NULL,0), -- S10: 预置充电中锁定,另一个订单号占用,用于验证同卡并发锁卡失败(fail=4) (99213,'99213',NULL, 0.00,0.00,9933,NULL,1,1,'SD-OTHER-LOCK',0); -- ============ 第3节 充电授权(time_mode 1=全天 2=指定时段 3=指定电价时段; auto_charge 1=到期自动停充) ============ USE `charging_pile_other`; INSERT INTO `t_charge_authorization` (`id`,`company_id`,`time_mode`,`time_ranges`,`price_types`,`auto_charge`,`site_ids`,`dispatch_amount`,`del_flag`) VALUES (9921,9921,1,NULL, NULL, 0,'19',30.00,0), (9922,9922,1,NULL, NULL, 0,'19',30.00,0), -- S3: 当前时间-2h ~ 当前时间+3min(跨零点窗口退化为 00:00~23:59,由脚本推进 max_end_time 验证停充) (9923,9923,2, CASE WHEN HOUR(NOW()) < 3 OR HOUR(NOW()) >= 23 THEN '[{"startTime":"00:00","endTime":"23:59"}]' ELSE CONCAT('[{"startTime":"', DATE_FORMAT(NOW() - INTERVAL 2 HOUR, '%H:%i'), '","endTime":"', DATE_FORMAT(NOW() + INTERVAL 3 MINUTE, '%H:%i'), '"}]') END, NULL,1,'19',30.00,0), (9924,9924,1,NULL, NULL, 0,'25',30.00,0), (9925,9925,2,'[{"startTime":"00:00","endTime":"00:00"}]', NULL,0,'19',30.00,0), (9926,9926,1,NULL, NULL, 0,'19',30.00,0), (9927,9927,1,NULL, NULL, 0,'19',30.00,0), (9928,9928,1,NULL, NULL, 0,'19',30.00,0), (9929,9929,1,NULL, NULL, 0,'19',30.00,0), (9930,9930,1,NULL, NULL, 0,'19',30.00,0), -- S5b: 指定时段但未配置时间段 -> 服务端按放行处理 (9931,9931,2,NULL, NULL, 0,'19',30.00,0), (9932,9932,1,NULL, NULL, 0,'19',30.00,0), (9933,9933,1,NULL, NULL, 0,'19',30.00,0); -- ============ 第4节 公司优惠设置与计费模板 ============ -- preferential_type: 1=不优惠 2=服务费按比例折扣(service_discount_rate 百分比) 3=按计费模板优惠单价 USE `charging_pile_other`; INSERT INTO `t_company_preferential` (`id`,`company_id`,`preferential_type`,`service_discount_rate`,`billing_template_id`,`del_flag`) VALUES (9921,9929,2,50.00,NULL,0), -- S8b 优惠方式3 必须落在本脚本第4节创建的计费模板 9902 上(曾误配其它模板导致查不到、按原价结算) (9922,9930,3,NULL,9902,0); USE `charging_pile_service`; INSERT INTO `t_billing_template` (`id`,`name`,`description`,`discount`,`del_flag`) VALUES (9902,'SD测试-模板优惠','刷卡充电流程测试 S8b',NULL,0); INSERT INTO `t_billing_template_detail` (`id`,`template_id`,`type`,`start_time`,`end_time`,`electrovalence`,`service_charge`,`cost_service_charge`,`del_flag`) VALUES (9904,9902,4,'00:00','08:00',0.2000,0.3000,0.3000,0), (9905,9902,3,'08:00','10:00',0.5000,0.3000,0.3000,0), (9906,9902,2,'10:00','12:00',0.9000,0.3000,0.3000,0); -- ============ 第5节 回查(执行后应输出 13 家公司 / 13 张卡 / 13 条授权 / 2 条优惠 / 模板3明细) ============ SELECT 'COMPANY' AS tag, id, name, account_mode, available_balance, locked_amount, status FROM charging_pile_other.t_group_company WHERE id BETWEEN 9921 AND 9933 ORDER BY id; SELECT 'CARD' AS tag, id, card_no, company_id, balance, locked_amount, status, charging_flag, charging_order_code FROM charging_pile_account.t_charging_card WHERE id BETWEEN 99201 AND 99213 ORDER BY id; SELECT 'AUTH' AS tag, id, company_id, time_mode, time_ranges, auto_charge, site_ids, dispatch_amount FROM charging_pile_other.t_charge_authorization WHERE id BETWEEN 9921 AND 9933 ORDER BY id; SELECT 'PREF' AS tag, id, company_id, preferential_type, service_discount_rate, billing_template_id FROM charging_pile_other.t_company_preferential WHERE id BETWEEN 9921 AND 9922 ORDER BY id; SELECT 'TPL' AS tag, id, template_id, type, start_time, end_time, electrovalence, service_charge FROM charging_pile_service.t_billing_template_detail WHERE template_id = 9902 ORDER BY id; SELECT 'GUN' AS tag, g.id, g.code, g.status, g.site_id, p.code AS pile_code FROM charging_pile_service.t_charging_gun g JOIN charging_pile_service.t_charging_pile p ON p.id = g.charging_pile_id WHERE g.del_flag = 0 AND p.del_flag = 0 AND g.site_id = 19 ORDER BY g.id;