| 1 |
- -- =============================================\n-- 智慧水务管理系统 - 支付功能增强\n-- 版本: V2\n-- =============================================\n\n-- 增强缴费记录表,添加更多支付渠道信息\nALTER TABLE rev_payment ADD COLUMN IF NOT EXISTS transaction_id VARCHAR(100);\nALTER TABLE rev_payment ADD COLUMN IF NOT EXISTS operator_id BIGINT;\nALTER TABLE rev_payment ADD COLUMN IF NOT EXISTS remark VARCHAR(500);\n\n-- 创建支付方式枚举\nCREATE TABLE IF NOT EXISTS rev_payment_method (\n id BIGSERIAL PRIMARY KEY,\n method_code VARCHAR(20) UNIQUE NOT NULL,\n method_name VARCHAR(50) NOT NULL,\n method_type VARCHAR(20), -- cash/electronic/bank\n enabled BOOLEAN DEFAULT true,\n description TEXT,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n);\n\n-- 创建支付渠道表\nCREATE TABLE IF NOT EXISTS rev_payment_channel (\n id BIGSERIAL PRIMARY KEY,\n method_code VARCHAR(20) NOT NULL,\n channel_code VARCHAR(30) UNIQUE NOT NULL,\n channel_name VARCHAR(50) NOT NULL,\n channel_type VARCHAR(20), -- pos/app/web/mini/counter\n config JSONB, -- 渠道配置信息\n enabled BOOLEAN DEFAULT true,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n);\n\n-- 创建支付统计表\nCREATE TABLE IF NOT EXISTS rev_payment_stat (\n id BIGSERIAL PRIMARY KEY,\n date DATE NOT NULL,\n method_code VARCHAR(20),\n channel_code VARCHAR(30),\n payment_count BIGINT DEFAULT 0,\n payment_amount DECIMAL(15,2) DEFAULT 0,\n success_count BIGINT DEFAULT 0,\n success_amount DECIMAL(15,2) DEFAULT 0,\n fail_count BIGINT DEFAULT 0,\n fail_amount DECIMAL(15,2) DEFAULT 0,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW(),\n UNIQUE(date, method_code, channel_code)\n);\n\n-- 添加支付方式枚举数据\nINSERT INTO rev_payment_method (method_code, method_name, method_type, description) VALUES \n('counter', '柜台支付', 'cash', '营业厅柜台现金支付'),\n('pos', 'POS支付', 'electronic', 'POS机刷卡支付'),\n('alipay', '支付宝', 'electronic', '支付宝移动支付'),\n('wechat', '微信支付', 'electronic', '微信移动支付'),\n('bank_transfer', '银行转账', 'bank', '银行转账支付')\nON CONFLICT (method_code) DO NOTHING;\n\n-- 添加支付渠道数据\nINSERT INTO rev_payment_channel (method_code, channel_code, channel_name, channel_type) VALUES \n('counter', 'counter_cash', '柜台现金', 'counter'),\n('counter', 'counter_card', '柜台刷卡', 'counter'),\n('pos', 'pos_counter', '柜台POS', 'pos'),\n('pos', 'pos_mobile', '移动POS', 'pos'),\n('alipay', 'alipay_app', '支付宝APP', 'app'),\n('alipay', 'alipay_wap', '支付宝网页', 'web'),\n('alipay', 'alipay_qr', '支付宝二维码', 'pos'),\n('wechat', 'wechat_app', '微信APP', 'app'),\n('wechat', 'wechat_wap', '微信网页', 'web'),\n('wechat', 'wechat_qr', '微信二维码', 'pos'),\n('wechat', 'wechat_mini', '微信小程序', 'mini'),\n('bank_transfer', 'counter_transfer', '柜台转账', 'counter'),\n('bank_transfer', 'online_transfer', '网上银行', 'web')\nON CONFLICT (channel_code) DO NOTHING;\n\n-- 创建支付流水表\nCREATE TABLE IF NOT EXISTS rev_payment_flow (\n id BIGSERIAL PRIMARY KEY,\n payment_no VARCHAR(50) UNIQUE NOT NULL,\n order_no VARCHAR(100),\n method_code VARCHAR(20),\n channel_code VARCHAR(30),\n amount DECIMAL(10,2) NOT NULL,\n status VARCHAR(20) NOT NULL, -- pending/success/failed/cancelled\n request_data JSONB,\n response_data JSONB,\n error_message TEXT,\n created_at TIMESTAMP DEFAULT NOW(),\n updated_at TIMESTAMP DEFAULT NOW()\n);\n\n-- 创建账单状态变更日志表\nCREATE TABLE IF NOT EXISTS rev_bill_status_log (\n id BIGSERIAL PRIMARY KEY,\n bill_id BIGINT NOT NULL,\n old_status VARCHAR(20),\n new_status VARCHAR(20) NOT NULL,\n operator_id BIGINT,\n remark TEXT,\n created_at TIMESTAMP DEFAULT NOW(),\n FOREIGN KEY (bill_id) REFERENCES rev_bill(id)\n);\n\n-- 添加索引\nCREATE INDEX IF NOT EXISTS idx_payment_method ON rev_payment(method_code);\nCREATE INDEX IF NOT EXISTS idx_payment_channel ON rev_payment(channel_code);\nCREATE INDEX IF NOT EXISTS idx_payment_date ON rev_payment(paid_at);\nCREATE INDEX IF NOT EXISTS idx_bill_status_log_bill ON rev_bill_status_log(bill_id);\nCREATE INDEX IF NOT EXISTS idx_payment_flow_status ON rev_payment_flow(status);\n\n-- 创建触发器:账单状态变更时自动记录日志\nCREATE OR REPLACE FUNCTION bill_status_changed()\nRETURNS TRIGGER AS $$\nBEGIN\n IF OLD.status IS DISTINCT FROM NEW.status THEN\n INSERT INTO rev_bill_status_log (bill_id, old_status, new_status, operator_id, remark)\n VALUES (NEW.id, OLD.status, NEW.status, NEW.updated_by, '自动更新状态');\n END IF;\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n\n-- 创建触发器:当账单状态变更时自动触发\nCREATE TRIGGER trg_bill_status_changed\n AFTER UPDATE ON rev_bill\n FOR EACH ROW\n EXECUTE FUNCTION bill_status_changed();\n\n-- 创建视图:客户缴费综合信息\nCREATE OR REPLACE VIEW v_customer_payment_summary AS\nSELECT \n c.customer_id,\n c.customer_no,\n c.customer_name,\n c.area,\n c.phone,\n COUNT(DISTINCT p.id) as payment_count,\n SUM(p.amount) as total_amount,\n COUNT(CASE WHEN p.status = 'success' THEN 1 END) as success_count,\n SUM(CASE WHEN p.status = 'success' THEN p.amount END) as success_amount,\n COUNT(CASE WHEN p.status = 'failed' THEN 1 END) as failed_count,\n SUM(CASE WHEN p.status = 'failed' THEN p.amount END) as failed_amount,\n MAX(p.paid_at) as last_payment_date\nFROM rev_customer c\nLEFT JOIN rev_payment p ON c.id = p.customer_id\nGROUP BY c.customer_id, c.customer_no, c.customer_name, c.area, c.phone;\n\n-- 创建视图:支付渠道统计\nCREATE OR REPLACE VIEW v_payment_channel_stats AS\nSELECT \n pc.method_code,\n pm.method_name,\n pc.channel_code,\n pc.channel_name,\n COUNT(p.id) as transaction_count,\n SUM(p.amount) as total_amount,\n AVG(p.amount) as avg_amount,\n MIN(p.paid_at) as first_payment,\n MAX(p.paid_at) as last_payment\nFROM rev_payment p\nJOIN rev_payment_channel pc ON p.pay_channel = pc.channel_code\nJOIN rev_payment_method pm ON pc.method_code = pm.method_code\nGROUP BY pc.method_code, pm.method_name, pc.channel_code, pc.channel_name\nORDER BY pc.method_code, total_amount DESC;\n\n-- 插入示例数据:测试支付渠道配置\nINSERT INTO rev_payment_method (method_code, method_name, method_type, description) VALUES \n('test_method', '测试支付方式', 'electronic', '用于测试的支付方式')\nON CONFLICT (method_code) DO NOTHING;\n\nINSERT INTO rev_payment_channel (method_code, channel_code, channel_name, channel_type, config) VALUES \n('test_method', 'test_counter', '测试柜台', 'counter', '{\"terminal_id\": \"TEST001\"}'),\n('test_method', 'test_online', '测试在线', 'web', '{\"api_url\": \"https://test.example.com/pay\"}')\nON CONFLICT (channel_code) DO NOTHING;\n\n-- 创建存储过程:批量生成账单\nCREATE OR REPLACE PROCEDURE generate_bills_by_period(p_period VARCHAR)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n v_reading_count BIGINT;\n v_bill_count BIGINT;\nBEGIN\n -- 获取需要生成账单的抄表记录数量\n SELECT COUNT(*) INTO v_reading_count\n FROM rev_reading r\n WHERE r.reading_period = p_period AND r.verified = 1\n AND NOT EXISTS (SELECT 1 FROM rev_bill b WHERE b.reading_id = r.id);\n \n -- 生成账单\n INSERT INTO rev_bill (bill_no, customer_id, meter_id, reading_id, bill_period, \n prev_reading, curr_reading, consumption, water_fee, sewage_fee, total_fee, status, due_date)\n SELECT \n 'BILL-' || EXTRACT(EPOCH FROM NOW())::BIGINT || '-' || r.id,\n m.customer_id,\n r.meter_id,\n r.id,\n r.reading_period,\n r.prev_reading,\n r.curr_reading,\n r.consumption,\n -- 计算水费(这里简化处理,实际应该根据阶梯水价计算)\n r.consumption * 0.45, -- 假设单价0.45元/立方米\n -- 污水处理费(按用水量的80%计算)\n r.consumption * 0.45 * 0.8,\n -- 总费用\n r.consumption * 0.45 + r.consumption * 0.45 * 0.8,\n 'pending',\n CURRENT_DATE + INTERVAL '30 days'\n FROM rev_reading r\n JOIN rev_meter m ON r.meter_id = m.id\n WHERE r.reading_period = p_period AND r.verified = 1\n AND NOT EXISTS (SELECT 1 FROM rev_bill b WHERE b.reading_id = r.id);\n \n -- 获取生成的账单数量\n GET DIAGNOSTICS v_bill_count = ROW_COUNT;\n \n -- 输出结果\n RAISE NOTICE 'Period: %, Readings: %, Bills Generated: %', p_period, v_reading_count, v_bill_count;\nEND;\n$$;\n\n-- 创建存储过程:批量处理欠费账单\nCREATE OR REPLACE PROCEDURE process_overdue_bills()\nLANGUAGE plpgsql\nAS $$\nBEGIN\n UPDATE rev_bill \n SET status = 'overdue', \n updated_at = CURRENT_TIMESTAMP\n WHERE status = 'pending' \n AND due_date < CURRENT_DATE;\n \n RAISE NOTICE 'Processed overdue bills';\nEND;\n$$;\n\n-- 创建存储过程:批量退款\nCREATE OR REPLACE PROCEDURE batch_refund(p_bill_ids BIGINT[], p_reason VARCHAR)\nLANGUAGE plpgsql\nAS $$\nDECLARE\n v_bill_record RECORD;\nBEGIN\n FOR v_bill_record IN SELECT * FROM unnest(p_bill_ids) AS id\n LEFT JOIN rev_bill ON id = rev_bill.id\n LOOP\n IF v_bill_record.rev_bill_id IS NOT NULL THEN\n -- 更新账单状态\n UPDATE rev_bill \n SET status = 'refunded', \n paid_fee = 0,\n updated_at = CURRENT_TIMESTAMP\n WHERE id = v_bill_record.id;\n \n -- 添加退款记录\n INSERT INTO rev_payment (bill_id, customer_id, payment_no, amount, pay_method, pay_channel, remark)\n SELECT id, customer_id, 'REFUND-' || EXTRACT(EPOCH FROM NOW())::BIGINT, paid_fee, 'refund', 'system', p_reason\n FROM rev_bill \n WHERE id = v_bill_record.id;\n END IF;\n END LOOP;\n \n RAISE NOTICE 'Processed % refunds', array_length(p_bill_ids, 1);\nEND;\n$$;\n\n-- 授权(如果需要)\n-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO water_revenue_user;\n-- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO water_revenue_user;\n\n-- 创建索引优化查询性能\nCREATE INDEX IF NOT EXISTS idx_bill_period_customer ON rev_bill(bill_period, customer_id);\nCREATE INDEX IF NOT EXISTS idx_bill_amount ON rev_bill(total_fee);\nCREATE INDEX IF NOT EXISTS idx_payment_amount ON rev_payment(amount);\n\n-- 添加注释\nCOMMENT ON TABLE rev_payment_method IS '支付方式配置表';\nCOMMENT ON TABLE rev_payment_channel IS '支付渠道配置表';\nCOMMENT ON TABLE rev_payment_stat IS '支付统计表';\nCOMMENT ON TABLE rev_payment_flow IS '支付流水表';\nCOMMENT ON TABLE rev_bill_status_log IS '账单状态变更日志表';\nCOMMENT ON VIEW v_customer_payment_summary IS '客户缴费综合信息视图';\nCOMMENT ON VIEW v_payment_channel_stats IS '支付渠道统计视图';\n\n-- 添加示例测试数据\n-- 测试支付记录\nINSERT INTO rev_payment (bill_id, customer_id, payment_no, amount, pay_method, pay_channel, paid_at) \nSELECT id, customer_id, 'PAY-TEST-' || EXTRACT(EPOCH FROM NOW())::BIGINT || i, 50.00, 'counter', 'counter_cash', CURRENT_DATE - INTERVAL '1 day'\nFROM rev_bill \nWHERE status = 'paid' AND id <= 10\nON CONFLICT (payment_no) DO NOTHING;\n\n-- 测试支付宝支付记录\nINSERT INTO rev_payment (bill_id, customer_id, payment_no, amount, pay_method, pay_channel, paid_at) \nSELECT id, customer_id, 'PAY-ALI-' || EXTRACT(EPOCH FROM NOW())::BIGINT || i, 80.00, 'alipay', 'alipay_app', CURRENT_DATE - INTERVAL '2 days'\nFROM rev_bill \nWHERE status = 'paid' AND id BETWEEN 11 AND 20\nON CONFLICT (payment_no) DO NOTHING;\n\n-- 测试微信支付记录\nINSERT INTO rev_payment (bill_id, customer_id, payment_no, amount, pay_method, pay_channel, paid_at) \nSELECT id, customer_id, 'PAY-WX-' || EXTRACT(EPOCH FROM NOW())::BIGINT || i, 120.00, 'wechat', 'wechat_qr', CURRENT_DATE - INTERVAL '3 days'\nFROM rev_bill \nWHERE status = 'paid' AND id BETWEEN 21 AND 30\nON CONFLICT (payment_no) DO NOTHING;\n\n-- 完成升级\n-- 升级完成时间记录\nINSERT INTO sys_upgrade_log (version, description, upgrade_time) \nVALUES ('V2', '支付功能增强 - 支持多渠道支付、自动账单生成、支付统计', NOW())\nON CONFLICT (version) DO NOTHING;\n\n-- 输出升级完成信息\nRAISE NOTICE 'Payment enhancement upgrade completed successfully!';\nRAISE NOTICE 'Added tables: rev_payment_method, rev_payment_channel, rev_payment_stat, rev_payment_flow, rev_bill_status_log';\nRAISE NOTICE 'Added views: v_customer_payment_summary, v_payment_channel_stats';\nRAISE NOTICE 'Added procedures: generate_bills_by_period, process_overdue_bills, batch_refund';\nRAISE NOTICE 'Added triggers: trg_bill_status_changed';\nRAISE NOTICE 'Added indexes for performance optimization';\n
|