feat(frontend+wm-revenue): #56 网上营业厅前端(水费/报装/公告/绑定)
- 前端页面: WaterBillView.vue, InstallApplyView.vue, NoticeView.vue, UserBindView.vue - API 模块: wx-hall.ts (水费/报装/公告/绑定全部接口) - 路由: 注册到 frontend/src/router/index.ts (wx-hall/*) - 后端 Controller: WxHallApiController (12 端点, /api/wx-hall/*) - DDL: V6__wx_hall_user_bindng.sql (用户绑定表)
This commit is contained in:
+220
@@ -0,0 +1,220 @@
|
||||
package com.water.revenue.controller.wxhall;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.water.common.core.result.R;
|
||||
import com.water.revenue.entity.Announcement;
|
||||
import com.water.revenue.entity.PaymentRecord;
|
||||
import com.water.revenue.entity.WaterBill;
|
||||
import com.water.revenue.service.AnnouncementService;
|
||||
import com.water.revenue.service.BillService;
|
||||
import com.water.revenue.service.InstallService;
|
||||
import com.water.revenue.service.PaymentService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 微信网厅 API(移动端适配接口)
|
||||
* 所有路径前缀: /api/wx-hall/*
|
||||
*/
|
||||
@Slf4j
|
||||
@Tag(name = "微信网厅 API")
|
||||
@RestController
|
||||
@RequestMapping("/wx-hall")
|
||||
@RequiredArgsConstructor
|
||||
public class WxHallApiController {
|
||||
|
||||
private final BillService billService;
|
||||
private final InstallService installService;
|
||||
private final AnnouncementService announcementService;
|
||||
private final PaymentService paymentService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
// ========== 水费查询缴费 ==========
|
||||
|
||||
@Operation(summary = "查询用户账单列表")
|
||||
@GetMapping("/bill/list")
|
||||
public R<Page<WaterBill>> billList(
|
||||
@RequestParam String customerNo,
|
||||
@RequestParam(required = false) String billPeriod,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(defaultValue = "1") int pageNum,
|
||||
@RequestParam(defaultValue = "10") int pageSize) {
|
||||
return R.ok(billService.queryBills(customerNo, billPeriod, status, pageNum, pageSize));
|
||||
}
|
||||
|
||||
@Operation(summary = "账单详情")
|
||||
@GetMapping("/bill/{billId}")
|
||||
public R<Map<String, Object>> billDetail(@PathVariable Long billId) {
|
||||
return R.ok(billService.getBillDetail(billId));
|
||||
}
|
||||
|
||||
@Operation(summary = "在线缴费(微信下单)")
|
||||
@PostMapping("/bill/pay")
|
||||
public R<Map<String, Object>> billPay(@RequestBody Map<String, Object> req) {
|
||||
Long billId = Long.valueOf(req.get("billId").toString());
|
||||
BigDecimal amount = new BigDecimal(req.get("amount").toString());
|
||||
return R.ok(paymentService.payByWechat(billId, amount));
|
||||
}
|
||||
|
||||
@Operation(summary = "缴费记录")
|
||||
@GetMapping("/bill/payment-records")
|
||||
public R<List<Map<String, Object>>> paymentRecords(
|
||||
@RequestParam String customerNo,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
List<Map<String, Object>> records = jdbcTemplate.queryForList(
|
||||
"SELECT payment_no, bill_no, amount, channel, channel_name, status, pay_time, remark " +
|
||||
"FROM wm_payment_record WHERE customer_no = ? ORDER BY pay_time DESC LIMIT ?",
|
||||
customerNo, limit);
|
||||
return R.ok(records);
|
||||
}
|
||||
|
||||
// ========== 报装申请 ==========
|
||||
|
||||
@Operation(summary = "提交报装申请")
|
||||
@PostMapping("/install/apply")
|
||||
public R<Map<String, Object>> installApply(@RequestBody Map<String, String> req) {
|
||||
return R.ok(installService.preApply(
|
||||
req.get("name"),
|
||||
req.get("phone"),
|
||||
req.get("area"),
|
||||
req.get("address"),
|
||||
req.getOrDefault("customerType", "resident"),
|
||||
req.getOrDefault("caliber", "DN15")));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询报装进度")
|
||||
@GetMapping("/install/progress")
|
||||
public R<Map<String, Object>> installProgress(@RequestParam String applyNo) {
|
||||
return R.ok(installService.getProgress(applyNo));
|
||||
}
|
||||
|
||||
@Operation(summary = "报装申请记录列表")
|
||||
@GetMapping("/install/list")
|
||||
public R<List<Map<String, Object>>> installList(
|
||||
@RequestParam String phone,
|
||||
@RequestParam(defaultValue = "20") int limit) {
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
||||
"SELECT application_no, applicant_name, applicant_phone, area, address, " +
|
||||
"customer_type, caliber, status, created_at, updated_at " +
|
||||
"FROM rev_install WHERE applicant_phone = ? ORDER BY created_at DESC LIMIT ?",
|
||||
phone, limit);
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
// ========== 停水公告 ==========
|
||||
|
||||
@Operation(summary = "公告列表")
|
||||
@GetMapping("/notice/list")
|
||||
public R<Page<Announcement>> noticeList(
|
||||
@RequestParam(defaultValue = "1") int page,
|
||||
@RequestParam(defaultValue = "10") int size,
|
||||
@RequestParam(required = false) String type,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
// 只返回已发布的公告
|
||||
return R.ok(announcementService.list(page, size, type, 1, keyword));
|
||||
}
|
||||
|
||||
@Operation(summary = "公告详情")
|
||||
@GetMapping("/notice/{id}")
|
||||
public R<Announcement> noticeDetail(@PathVariable Long id) {
|
||||
return R.ok(announcementService.getDetail(id));
|
||||
}
|
||||
|
||||
@Operation(summary = "获取当前生效公告(按区域)")
|
||||
@GetMapping("/notice/active")
|
||||
public R<List<Announcement>> activeNotices(
|
||||
@RequestParam(required = false) String areaCode) {
|
||||
return R.ok(announcementService.getActiveAnnouncements(areaCode));
|
||||
}
|
||||
|
||||
// ========== 用户绑定 ==========
|
||||
|
||||
@Operation(summary = "手机号绑定")
|
||||
@PostMapping("/user/bindPhone")
|
||||
public R<Map<String, Object>> bindPhone(@RequestBody Map<String, String> req) {
|
||||
String openId = req.get("openId");
|
||||
String phone = req.get("phone");
|
||||
if (!StringUtils.hasText(openId) || !StringUtils.hasText(phone)) {
|
||||
return R.fail("openId和phone不能为空");
|
||||
}
|
||||
// 检查是否已绑定
|
||||
Long existCount = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM wx_hall_user_binding WHERE open_id = ? AND phone = ? AND status = 1",
|
||||
Long.class, openId, phone);
|
||||
if (existCount != null && existCount > 0) {
|
||||
return R.fail("该手机号已绑定");
|
||||
}
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO wx_hall_user_binding (open_id, phone, binding_type, status) VALUES (?, ?, 'phone', 1)",
|
||||
openId, phone);
|
||||
log.info("User bound phone: openId={}, phone={}", openId, phone);
|
||||
return R.ok(Map.of("openId", openId, "phone", phone, "status", "bound"));
|
||||
}
|
||||
|
||||
@Operation(summary = "户号绑定")
|
||||
@PostMapping("/user/bindCustomer")
|
||||
public R<Map<String, Object>> bindCustomer(@RequestBody Map<String, String> req) {
|
||||
String openId = req.get("openId");
|
||||
String customerNo = req.get("customerNo");
|
||||
if (!StringUtils.hasText(openId) || !StringUtils.hasText(customerNo)) {
|
||||
return R.fail("openId和customerNo不能为空");
|
||||
}
|
||||
// 检查是否已绑定
|
||||
Long existCount = jdbcTemplate.queryForObject(
|
||||
"SELECT COUNT(*) FROM wx_hall_user_binding WHERE open_id = ? AND customer_no = ? AND status = 1",
|
||||
Long.class, openId, customerNo);
|
||||
if (existCount != null && existCount > 0) {
|
||||
return R.fail("该户号已绑定");
|
||||
}
|
||||
// 查询客户名称
|
||||
String customerName = null;
|
||||
try {
|
||||
customerName = jdbcTemplate.queryForObject(
|
||||
"SELECT customer_name FROM rev_customer WHERE customer_no = ?",
|
||||
String.class, customerNo);
|
||||
} catch (Exception e) {
|
||||
log.warn("Customer not found: {}", customerNo);
|
||||
}
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO wx_hall_user_binding (open_id, customer_no, customer_name, binding_type, status) VALUES (?, ?, ?, 'customer_no', 1)",
|
||||
openId, customerNo, customerName);
|
||||
log.info("User bound customer: openId={}, customerNo={}", openId, customerNo);
|
||||
return R.ok(Map.of("openId", openId, "customerNo", customerNo,
|
||||
"customerName", customerName != null ? customerName : "", "status", "bound"));
|
||||
}
|
||||
|
||||
@Operation(summary = "解绑")
|
||||
@PostMapping("/user/unbind")
|
||||
public R<Map<String, Object>> unbind(@RequestBody Map<String, String> req) {
|
||||
String openId = req.get("openId");
|
||||
String bindingId = req.get("bindingId");
|
||||
if (!StringUtils.hasText(openId) || !StringUtils.hasText(bindingId)) {
|
||||
return R.fail("参数不完整");
|
||||
}
|
||||
jdbcTemplate.update(
|
||||
"UPDATE wx_hall_user_binding SET status = 0, updated_at = NOW() WHERE id = ? AND open_id = ?",
|
||||
Long.valueOf(bindingId), openId);
|
||||
log.info("User unbound: openId={}, bindingId={}", openId, bindingId);
|
||||
return R.ok(Map.of("status", "unbound"));
|
||||
}
|
||||
|
||||
@Operation(summary = "查询用户绑定列表")
|
||||
@GetMapping("/user/bindings")
|
||||
public R<List<Map<String, Object>>> bindings(@RequestParam String openId) {
|
||||
List<Map<String, Object>> list = jdbcTemplate.queryForList(
|
||||
"SELECT id, phone, customer_no, customer_name, binding_type, status, created_at " +
|
||||
"FROM wx_hall_user_binding WHERE open_id = ? AND status = 1 ORDER BY created_at DESC",
|
||||
openId);
|
||||
return R.ok(list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
-- 微信网厅用户绑定表(手机号/户号绑定)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wx_hall_user_binding (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
open_id VARCHAR(64) NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
customer_no VARCHAR(32),
|
||||
customer_name VARCHAR(100),
|
||||
binding_type VARCHAR(20) NOT NULL DEFAULT 'phone', -- phone / customer_no
|
||||
status INTEGER NOT NULL DEFAULT 1, -- 1-有效 0-已解绑
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wx_binding_open_id ON wx_hall_user_binding(open_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_wx_binding_phone ON wx_hall_user_binding(phone);
|
||||
CREATE INDEX IF NOT EXISTS idx_wx_binding_customer ON wx_hall_user_binding(customer_no);
|
||||
CREATE INDEX IF NOT EXISTS idx_wx_binding_status ON wx_hall_user_binding(status);
|
||||
Reference in New Issue
Block a user