fujian_water_biz_doc/water_biz_interface_design.md

31 KiB
Raw Blame History

福建水务营收系统接口设计文档

文档信息

项目信息 详情
项目名称 福建水务营收系统
文档类型 概要设计文档
技术框架 RuoYi-Vue-Pro + yudao-ui-admin-vue3
文档版本 v1.0
编写日期 2024-12-19
文档状态 🟡 进行中

目录

接口概述

福建水务业务系统提供丰富的接口用于与外部系统集成以及系统内部各模块间的数据交换。接口设计遵循标准化、安全性、可扩展性的原则基于RuoYi-Vue-Pro框架采用RESTful风格设计支持JSON数据格式。

接口设计原则

  • 统一性:所有接口遵循统一的设计规范和数据格式
  • 安全性:接口通过认证授权、参数校验等机制保障安全
  • 可维护性:接口文档完善,便于维护和升级
  • 兼容性:接口设计考虑版本兼容,支持平滑升级
  • 性能优化:接口设计考虑性能,支持缓存、分页等机制

RESTful API规范

系统API接口采用RESTful风格设计主要规范如下

资源命名

  • 使用名词复数表示资源集合,如/users/meters
  • 使用资源ID标识特定资源/users/1/meters/123
  • 资源层级关系通过路径嵌套表示,如/users/1/meters

HTTP方法

  • GET获取资源
  • POST创建资源
  • PUT更新资源全量更新
  • PATCH部分更新资源
  • DELETE删除资源

状态码

  • 200 OK请求成功
  • 201 Created资源创建成功
  • 400 Bad Request请求参数错误
  • 401 Unauthorized未授权
  • 403 Forbidden权限不足
  • 404 Not Found资源不存在
  • 500 Internal Server Error服务器内部错误

响应格式

系统统一采用以下JSON格式响应

{
  "code": 0,           // 业务状态码0表示成功非0表示失败
  "data": {},          // 响应数据
  "msg": "success"     // 响应消息
}

分页查询响应格式:

{
  "code": 0,
  "data": {
    "list": [],        // 数据列表
    "total": 100,      // 总记录数
    "pageNum": 1,      // 当前页码
    "pageSize": 10     // 每页记录数
  },
  "msg": "success"
}

接口文档

系统使用Knife4j基于Swagger自动生成API文档文档地址为http://{系统地址}/doc.html

主要特点:

  • 在线接口文档:支持在线查看接口定义
  • 接口调试:支持在线调试接口
  • 文档导出支持导出OpenAPI规范文档
  • 权限控制:支持对接口文档的访问控制

外部接口

银行接口对接

银行代扣接口

功能描述:通过银行系统自动从用户账户中扣除水费。

接口详情

  • 接口方式文件交换FTP/SFTP
  • 数据格式:定长文本文件
  • 交换频率每日凌晨2:00
  • 文件编码GBK

代扣文件格式

记录类型(1位) + 客户号(12位) + 户名(30位) + 银行账号(20位) + 扣款金额(12位,含2位小数) + 账期(6位) + 保留字段(19位)

代扣文件示例

1C00000000001张三                          62172511001234567890000009180202412          
1C00000000002李四                          62172511001234567891000015460202412          

回盘文件格式

记录类型(1位) + 客户号(12位) + 银行账号(20位) + 扣款金额(12位) + 处理状态(1位) + 银行流水号(20位) + 处理时间(14位) + 失败原因(20位)

代扣文件生成流程

  1. 每日凌晨2点自动生成代扣文件
  2. 查询当日待代扣账单数据
  3. 按银行要求格式生成文件内容
  4. 通过SFTP上传至银行服务器
  5. 记录文件生成和上传日志

银行实时缴费接口

功能描述:用户在银行柜台、网上银行或手机银行实时缴纳水费。

接口详情

  • 接口方式HTTP POST
  • 请求URLhttps://bank.api.com/payment/water-fee
  • 数据格式JSON
  • 认证方式API Key + 签名

请求参数

{
  "merchantId": "WATER001",
  "customerCode": "C001",
  "billCodes": ["B202412190001"],
  "totalAmount": 91.80,
  "bankAccount": "6217251100123456789",
  "customerName": "张三",
  "timestamp": "20241219103000",
  "signature": "ABC123DEF456..."
}

响应参数

{
  "resultCode": "0000",
  "resultMsg": "交易成功",
  "data": {
    "transactionId": "TXN20241219001",
    "paymentTime": "20241219103001",
    "bankSerial": "BNK20241219001234"
  }
}

支付宝接口对接

功能描述用户通过支付宝缴纳水费支持扫码支付和H5支付。

接口详情

  • 接口方式HTTP POST
  • 支付方式统一收单交易预创建alipay.trade.precreate
  • 数据格式JSON
  • 认证方式RSA2签名

预创建支付请求参数

{
  "app_id": "2021001234567890",
  "method": "alipay.trade.precreate", 
  "charset": "UTF-8",
  "sign_type": "RSA2",
  "timestamp": "2024-12-19 10:30:00",
  "version": "1.0",
  "notify_url": "https://water.example.com/api/payment/alipay/notify",
  "biz_content": {
    "out_trade_no": "P202412190002",
    "total_amount": "91.80",
    "subject": "水费缴费",
    "body": "2024年12月水费-客户编号:C001",
    "store_id": "WATER_STORE_001",
    "timeout_express": "30m"
  }
}

支付宝响应参数

{
  "alipay_trade_precreate_response": {
    "code": "10000",
    "msg": "Success",
    "out_trade_no": "P202412190002",
    "qr_code": "https://qr.alipay.com/bax08945xtdnfwgqmwi200b4"
  },
  "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
}

支付宝支付集成流程

  1. 调用支付宝预创建接口生成支付二维码
  2. 前端展示二维码供用户扫码支付
  3. 支付完成后支付宝发送异步通知
  4. 系统验证通知签名并更新订单状态
  5. 记录支付日志和账务处理

微信支付接口对接

功能描述:用户通过微信支付缴纳水费,支持扫码支付和小程序支付。

接口详情

  • 接口方式HTTP POST
  • 支付方式Native支付扫码/ JSAPI支付小程序
  • 请求URLhttps://api.mch.weixin.qq.com/v3/pay/transactions/native
  • 数据格式JSON
  • 认证方式微信支付V3签名

统一下单请求参数

{
  "appid": "wx8888888888888888",
  "mchid": "1900000109", 
  "description": "水费缴费-2024年12月",
  "out_trade_no": "P202412190003",
  "notify_url": "https://water.example.com/api/payment/wechat/notify",
  "amount": {
    "total": 9180,
    "currency": "CNY"
  },
  "attach": "客户编号:C001,账单号:B202412190001",
  "goods_tag": "WATER_FEE",
  "time_expire": "2024-12-19T11:00:00+08:00"
}

微信支付响应参数

{
  "code_url": "weixin://wxpay/bizpayurl?pr=HuaLcAKwa"
}

支付结果通知参数

{
  "id": "EV-2018022511223320873",
  "create_time": "2024-12-19T10:30:00+08:00",
  "resource_type": "encrypt-resource",
  "event_type": "TRANSACTION.SUCCESS",
  "summary": "支付成功",
  "resource": {
    "original_type": "transaction",
    "algorithm": "AEAD_AES_256_GCM",
    "ciphertext": "...",
    "associated_data": "transaction",
    "nonce": "..."
  }
}

短信接口

功能描述:向用户发送各类业务通知短信。

接口规范

  • 接口方式HTTP接口
  • 数据格式JSON
  • 交换频率:实时

物联网集抄平台接口

功能描述:与物联网集抄平台交互,获取智能水表数据。

接口规范

  • 接口方式HTTP接口或WebService
  • 数据格式JSON或XML
  • 交换频率:定时或实时

内部接口

客户管理API接口

客户信息查询接口

功能描述根据客户ID查询客户详细信息。

接口详情

  • 请求方式GET
  • 请求路径/admin-api/water/customer/{id}
  • 请求头Authorization: Bearer {token}

请求参数

参数名 类型 必填 说明 示例
id Long 客户ID 1

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "id": 1,
    "customerCode": "C001",
    "customerName": "张三",
    "customerType": "RESIDENT",
    "phone": "13800138000",
    "address": "福建省福州市台江区XX街道XX号",
    "status": 1,
    "createTime": "2024-12-19 10:00:00"
  }
}

RuoYi-Vue-Pro代码示例

@RestController
@RequestMapping("/admin-api/water/customer")
@Tag(name = "管理后台 - 客户管理")
@Validated
public class CustomerController {
    
    @Resource
    private CustomerService customerService;
    
    @GetMapping("/{id}")
    @Operation(summary = "获得客户")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('water:customer:query')")
    public CommonResult<CustomerRespVO> getCustomer(@PathVariable("id") Long id) {
        CustomerDO customer = customerService.getCustomer(id);
        return success(BeanUtils.toBean(customer, CustomerRespVO.class));
    }
}

客户分页查询接口

功能描述:分页查询客户列表信息。

接口详情

  • 请求方式GET
  • 请求路径/admin-api/water/customer/page

请求参数

参数名 类型 必填 说明 示例
pageNo Integer 页码默认1 1
pageSize Integer 每页条数默认10 10
customerName String 客户名称 张三
customerCode String 客户编号 C001
customerType String 客户类型 RESIDENT
phone String 联系电话 138

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "list": [
      {
        "id": 1,
        "customerCode": "C001",
        "customerName": "张三",
        "customerType": "RESIDENT",
        "phone": "13800138000",
        "address": "福建省福州市台江区XX街道XX号",
        "status": 1,
        "createTime": "2024-12-19 10:00:00"
      }
    ],
    "total": 1
  }
}

客户创建接口

功能描述:创建新客户记录。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/customer/create

请求参数

{
  "customerCode": "C002",
  "customerName": "李四",
  "customerType": "RESIDENT",
  "idType": "ID_CARD",
  "idNumber": "350103199001011234",
  "phone": "13900139000",
  "address": "福建省福州市鼓楼区XX街道XX号"
}

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": 2
}

客户管理服务实现要点

  • 校验客户编号唯一性
  • 数据对象转换和持久化
  • 业务异常处理和日志记录

水表管理API接口

水表信息查询接口

功能描述根据水表ID查询水表详细信息。

接口详情

  • 请求方式GET
  • 请求路径/admin-api/water/meter/{id}
  • 请求头Authorization: Bearer {token}

请求参数

参数名 类型 必填 说明 示例
id Long 水表ID 1

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "id": 1,
    "meterCode": "M001",
    "meterNo": "20241219001",
    "meterType": "SMART",
    "meterModel": "LXSY-15E",
    "meterCaliber": "15mm",
    "installDate": "2024-01-15",
    "installPosition": "1层水表井",
    "initialReading": 0.00,
    "currentReading": 156.32,
    "readingCycle": "MONTHLY",
    "meterStatus": 1,
    "customerId": 1,
    "customerName": "张三"
  }
}

水表管理控制器要点

  • 支持权限控制和参数校验
  • 标准的RESTful接口设计
  • 统一的响应格式和异常处理

抄表记录创建接口

功能描述:创建新的抄表记录。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/reading/create

请求参数

{
  "meterId": 1,
  "readingDate": "2024-12-19",
  "readingValue": 156.32,
  "readingType": "MANUAL",
  "readerId": "R001",
  "photoUrl": "https://example.com/photos/reading001.jpg",
  "remark": "正常抄表"
}

响应参数

{
  "code": 0,
  "msg": "操作成功", 
  "data": 1
}

抄表记录服务实现要点

  • 事务控制确保数据一致性
  • 水表存在性和读数合理性校验
  • 自动生成抄表编号和客户关联
  • 计算用水量并更新水表当前读数

抄表数据批量导入接口

功能描述批量导入抄表数据支持Excel文件上传。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/reading/import
  • Content-Typemultipart/form-data

请求参数

参数名 类型 必填 说明 示例
file MultipartFile Excel文件 reading_data.xlsx
updateSupport Boolean 是否更新已有数据 false

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "successCount": 95,
    "failureCount": 5,
    "failureList": [
      {
        "lineNumber": 3,
        "meterCode": "M003", 
        "errorMsg": "水表不存在"
      }
    ]
  }
}

账单管理API接口

账单查询接口

功能描述根据客户ID和查询条件查询账单信息。

接口详情

  • 请求方式GET
  • 请求路径/admin-api/water/bill/page

请求参数

参数名 类型 必填 说明 示例
pageNo Integer 页码默认1 1
pageSize Integer 每页条数默认10 10
customerId Long 客户ID 1
billMonth String 账期 2024-12
billStatus Integer 账单状态 0

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "list": [
      {
        "id": 1,
        "billCode": "B202412190001",
        "billMonth": "2024-12",
        "billDate": "2024-12-19",
        "waterUsage": 25.50,
        "waterFee": 76.50,
        "sewageFee": 15.30,
        "totalAmount": 91.80,
        "paidAmount": 0.00,
        "balanceAmount": 91.80,
        "dueDate": "2025-01-19",
        "billStatus": 0,
        "customerName": "张三",
        "meterCode": "M001"
      }
    ],
    "total": 1
  }
}

账单生成接口

功能描述:根据抄表记录生成水费账单。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/bill/generate

请求参数

{
  "billMonth": "2024-12",
  "customerIds": [1, 2, 3],
  "readingIds": [1, 2, 3],
  "dueDate": "2025-01-19"
}

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "generateCount": 3,
    "successList": [
      {
        "customerId": 1,
        "billId": 1,
        "totalAmount": 91.80
      }
    ],
    "failureList": []
  }
}

Service层代码示例

@Service
@Validated
public class BillServiceImpl implements BillService {
    
    @Resource
    private BillMapper billMapper;
    @Resource
    private MeterReadingService readingService;
    @Resource
    private WaterPriceService priceService;
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public BillGenerateRespVO generateBills(BillGenerateReqVO generateReqVO) {
        BillGenerateRespVO result = new BillGenerateRespVO();
        List<BillGenerateDetailVO> successList = new ArrayList<>();
        List<BillGenerateDetailVO> failureList = new ArrayList<>();
        
        for (Long readingId : generateReqVO.getReadingIds()) {
            try {
                // 获取抄表记录
                MeterReadingDO reading = readingService.getReading(readingId);
                
                // 计算水费
                WaterFeeCalculateDTO feeResult = priceService.calculateWaterFee(
                    reading.getCustomerId(), reading.getWaterUsage());
                
                // 创建账单
                BillDO bill = new BillDO();
                bill.setBillCode(generateBillCode());
                bill.setBillMonth(generateReqVO.getBillMonth());
                bill.setCustomerId(reading.getCustomerId());
                bill.setMeterId(reading.getMeterId());
                bill.setReadingId(readingId);
                bill.setWaterUsage(reading.getWaterUsage());
                bill.setWaterFee(feeResult.getWaterFee());
                bill.setSewageFee(feeResult.getSewageFee());
                bill.setTotalAmount(feeResult.getTotalAmount());
                bill.setDueDate(generateReqVO.getDueDate());
                
                billMapper.insert(bill);
                
                successList.add(buildSuccessDetail(reading.getCustomerId(), 
                                                 bill.getId(), feeResult.getTotalAmount()));
                
            } catch (Exception e) {
                failureList.add(buildFailureDetail(readingId, e.getMessage()));
            }
        }
        
        result.setGenerateCount(successList.size());
        result.setSuccessList(successList);
        result.setFailureList(failureList);
        return result;
    }
}

缴费管理API接口

缴费处理接口

功能描述:处理客户缴费操作。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/payment/create

请求参数

{
  "customerId": 1,
  "billIds": [1, 2],
  "paymentType": "NORMAL",
  "paymentChannel": "CASH",
  "paymentAmount": 183.60,
  "actualAmount": 200.00,
  "operatorId": "OP001",
  "outletCode": "OUT001",
  "remark": "现金缴费"
}

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "paymentId": 1,
    "paymentCode": "P202412190001",
    "changeAmount": 16.40,
    "invoiceNo": "INV20241219001"
  }
}

在线支付接口

功能描述:处理在线支付(微信、支付宝等)。

接口详情

  • 请求方式POST
  • 请求路径/admin-api/water/payment/online-pay

请求参数

{
  "customerId": 1,
  "billIds": [1],
  "paymentChannel": "WECHAT",
  "paymentAmount": 91.80,
  "returnUrl": "https://water.example.com/payment/callback",
  "notifyUrl": "https://water.example.com/api/payment/notify"
}

响应参数

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "paymentCode": "P202412190002",
    "prepayId": "wx20241219001234567890",
    "payUrl": "weixin://wxpay/bizpayurl?pr=abc123",
    "qrCode": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
  }
}

工单接口

工单创建接口

功能描述:创建业务工单。

接口规范

  • 请求方式POST
  • 请求路径:/api/workorders
  • 请求/返回格式JSON

工单状态更新接口

功能描述:更新工单处理状态。

接口规范

  • 请求方式PUT
  • 请求路径:/api/workorders/{workorderId}/status
  • 请求/返回格式JSON

接口标准

接口协议

系统接口主要采用以下协议:

  • RESTful API:适用于系统内部模块间的交互以及移动应用等轻量级客户端
  • WebService:适用于与外部系统的集成,特别是银行等传统机构
  • 消息队列:适用于异步处理的场景,如批量数据处理、通知推送等

数据格式

接口数据主要采用以下格式:

  • JSON主要用于RESTful API接口结构简单清晰适合Web应用
  • XML主要用于WebService接口兼容性好适合与传统系统对接
  • 文本文件:主要用于批量数据交换,如银行代扣文件等

接口安全设计

接口安全采用多层防护机制:

认证机制

JWT令牌认证

@RestController
public class AuthController {
    
    @Resource
    private AuthService authService;
    
    @PostMapping("/admin-api/system/auth/login")
    public CommonResult<AuthLoginRespVO> login(@Valid @RequestBody AuthLoginReqVO reqVO) {
        // 验证用户名密码
        AdminUserDO user = authService.authenticate(reqVO.getUsername(), reqVO.getPassword());
        
        // 生成JWT Token
        String token = authService.createToken(user.getId(), user.getTenantId());
        
        return success(AuthLoginRespVO.builder()
            .userId(user.getId())
            .accessToken(token)
            .refreshToken(authService.createRefreshToken(user.getId()))
            .expiresTime(LocalDateTime.now().plusHours(2))
            .build());
    }
}

API Key认证(外部系统):

@Component
public class ApiKeyAuthenticationFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        String apiKey = request.getHeader("X-API-KEY");
        String timestamp = request.getHeader("X-TIMESTAMP");
        String signature = request.getHeader("X-SIGNATURE");
        
        // 验证API Key
        if (!apiKeyService.validateApiKey(apiKey)) {
            writeErrorResponse(response, "Invalid API Key");
            return;
        }
        
        // 验证时间戳(防重放攻击)
        if (!validateTimestamp(timestamp)) {
            writeErrorResponse(response, "Request expired");
            return;
        }
        
        // 验证签名
        if (!validateSignature(request, signature)) {
            writeErrorResponse(response, "Invalid signature");
            return;
        }
        
        filterChain.doFilter(request, response);
    }
}

数据加密

敏感数据加密

@Component
public class DataEncryptionService {
    
    private final AESUtil aesUtil;
    
    public String encryptPersonalInfo(String plainText) {
        if (StrUtil.isBlank(plainText)) {
            return plainText;
        }
        return aesUtil.encrypt(plainText);
    }
    
    public String decryptPersonalInfo(String cipherText) {
        if (StrUtil.isBlank(cipherText)) {
            return cipherText;
        }
        return aesUtil.decrypt(cipherText);
    }
}

访问控制

IP白名单控制

@Component
public class IpWhitelistFilter extends OncePerRequestFilter {
    
    @Value("${water.security.ip-whitelist}")
    private List<String> ipWhitelist;
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        String clientIp = getClientIpAddress(request);
        
        if (!isIpAllowed(clientIp)) {
            response.setStatus(HttpStatus.FORBIDDEN.value());
            response.getWriter().write("{\"code\":403,\"msg\":\"IP access denied\"}");
            return;
        }
        
        filterChain.doFilter(request, response);
    }
}

接口限流

基于Redis的令牌桶限流

@Component
public class RateLimitService {
    
    @Resource
    private StringRedisTemplate redisTemplate;
    
    public boolean allowRequest(String key, int maxRequests, Duration window) {
        String redisKey = "rate_limit:" + key;
        String script = """
            local key = KEYS[1]
            local window = tonumber(ARGV[1])
            local limit = tonumber(ARGV[2])
            local current = redis.call('get', key)
            if current == false then
                redis.call('setex', key, window, 1)
                return 1
            end
            if tonumber(current) < limit then
                return redis.call('incr', key)
            else
                return 0
            end
            """;
            
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
        Long result = redisTemplate.execute(redisScript, 
            Collections.singletonList(redisKey), 
            String.valueOf(window.getSeconds()),
            String.valueOf(maxRequests));
            
        return result != null && result > 0;
    }
}

错误处理机制

统一异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(ServiceException.class)
    public CommonResult<?> serviceExceptionHandler(ServiceException ex) {
        log.info("[serviceExceptionHandler]", ex);
        return CommonResult.error(ex.getCode(), ex.getMessage());
    }
    
    @ExceptionHandler(ConstraintViolationException.class)
    public CommonResult<?> constraintViolationExceptionHandler(ConstraintViolationException ex) {
        log.info("[constraintViolationExceptionHandler]", ex);
        return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不正确:" + ex.getMessage());
    }
    
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public CommonResult<?> methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException ex) {
        log.info("[methodArgumentNotValidExceptionHandler]", ex);
        FieldError fieldError = ex.getBindingResult().getFieldError();
        assert fieldError != null;
        return CommonResult.error(BAD_REQUEST.getCode(), "请求参数不正确:" + fieldError.getDefaultMessage());
    }
}

错误码定义

public interface ErrorCodeConstants {
    
    // ========== 通用错误码 1-000-000-000 ==========
    ErrorCode SUCCESS = new ErrorCode(0, "成功");
    ErrorCode BAD_REQUEST = new ErrorCode(400, "请求参数不正确");
    ErrorCode UNAUTHORIZED = new ErrorCode(401, "账号未登录");
    ErrorCode FORBIDDEN = new ErrorCode(403, "没有该操作权限");
    ErrorCode NOT_FOUND = new ErrorCode(404, "请求未找到");
    ErrorCode METHOD_NOT_ALLOWED = new ErrorCode(405, "请求方法不正确");
    ErrorCode INTERNAL_SERVER_ERROR = new ErrorCode(500, "系统异常");
    
    // ========== 客户管理错误码 1-001-000-000 ==========
    ErrorCode CUSTOMER_NOT_EXISTS = new ErrorCode(1_001_000_001, "客户不存在");
    ErrorCode CUSTOMER_CODE_DUPLICATE = new ErrorCode(1_001_000_002, "客户编号已存在");
    ErrorCode CUSTOMER_STATUS_INVALID = new ErrorCode(1_001_000_003, "客户状态不正确");
    
    // ========== 水表管理错误码 1-002-000-000 ==========
    ErrorCode METER_NOT_EXISTS = new ErrorCode(1_002_000_001, "水表不存在");
    ErrorCode METER_CODE_DUPLICATE = new ErrorCode(1_002_000_002, "水表编号已存在");
    ErrorCode METER_READING_INVALID = new ErrorCode(1_002_000_003, "水表读数不正确");
    
    // ========== 账单管理错误码 1-003-000-000 ==========
    ErrorCode BILL_NOT_EXISTS = new ErrorCode(1_003_000_001, "账单不存在");
    ErrorCode BILL_ALREADY_PAID = new ErrorCode(1_003_000_002, "账单已缴费");
    ErrorCode BILL_AMOUNT_INVALID = new ErrorCode(1_003_000_003, "账单金额不正确");
    
    // ========== 缴费管理错误码 1-004-000-000 ==========
    ErrorCode PAYMENT_FAILED = new ErrorCode(1_004_000_001, "缴费失败");
    ErrorCode PAYMENT_AMOUNT_INSUFFICIENT = new ErrorCode(1_004_000_002, "缴费金额不足");
    ErrorCode PAYMENT_CHANNEL_UNAVAILABLE = new ErrorCode(1_004_000_003, "缴费渠道不可用");
}

接口调用示例

成功响应示例

{
  "code": 0,
  "msg": "操作成功",
  "data": {
    "id": 1,
    "customerName": "张三"
  }
}

失败响应示例

{
  "code": 1001000001,
  "msg": "客户不存在",
  "data": null
}

前端接口调用示例

Vue3 + TypeScript接口封装

// api/water/customer.ts
import { request } from '@/utils/request'

export interface CustomerVO {
  id: number
  customerCode: string
  customerName: string
  customerType: string
  phone: string
  address: string
  status: number
  createTime: string
}

export interface CustomerPageReqVO extends PageParam {
  customerName?: string
  customerCode?: string
  customerType?: string
  phone?: string
}

export const CustomerApi = {
  // 获取客户分页
  getCustomerPage: (params: CustomerPageReqVO) => {
    return request.get<PageResult<CustomerVO>>({ url: '/water/customer/page', params })
  },
  
  // 获取客户详情
  getCustomer: (id: number) => {
    return request.get<CustomerVO>({ url: `/water/customer/${id}` })
  },
  
  // 创建客户
  createCustomer: (data: CustomerSaveReqVO) => {
    return request.post<number>({ url: '/water/customer/create', data })
  },
  
  // 更新客户
  updateCustomer: (data: CustomerSaveReqVO) => {
    return request.put<void>({ url: '/water/customer/update', data })
  },
  
  // 删除客户
  deleteCustomer: (id: number) => {
    return request.delete<void>({ url: `/water/customer/delete?id=${id}` })
  }
}

Vue组件使用示例

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { CustomerApi, CustomerVO } from '@/api/water/customer'
import { formatDate } from '@/utils/formatTime'

const customerList = ref<CustomerVO[]>([])
const loading = ref(true)
const total = ref(0)
const queryParams = ref({
  pageNo: 1,
  pageSize: 10,
  customerName: '',
  customerCode: ''
})

const getCustomerList = async () => {
  loading.value = true
  try {
    const data = await CustomerApi.getCustomerPage(queryParams.value)
    customerList.value = data.list
    total.value = data.total
  } catch (error) {
    console.error('获取客户列表失败:', error)
  } finally {
    loading.value = false
  }
}

const handleQuery = () => {
  queryParams.value.pageNo = 1
  getCustomerList()
}

const handleReset = () => {
  queryParams.value = {
    pageNo: 1,
    pageSize: 10,
    customerName: '',
    customerCode: ''
  }
  getCustomerList()
}

onMounted(() => {
  getCustomerList()
})
</script>

<template>
  <div class="app-container">
    <!-- 查询表单 -->
    <el-form :model="queryParams" ref="queryFormRef" inline>
      <el-form-item label="客户名称" prop="customerName">
        <el-input v-model="queryParams.customerName" placeholder="请输入客户名称" />
      </el-form-item>
      <el-form-item label="客户编号" prop="customerCode">
        <el-input v-model="queryParams.customerCode" placeholder="请输入客户编号" />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="handleQuery">搜索</el-button>
        <el-button @click="handleReset">重置</el-button>
      </el-form-item>
    </el-form>
    
    <!-- 数据表格 -->
    <el-table v-loading="loading" :data="customerList">
      <el-table-column label="客户编号" prop="customerCode" />
      <el-table-column label="客户名称" prop="customerName" />
      <el-table-column label="联系电话" prop="phone" />
      <el-table-column label="创建时间" prop="createTime" :formatter="formatDate" />
    </el-table>
    
    <!-- 分页组件 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="getCustomerList"
    />
  </div>
</template>