Skip to main content

sz_rust_core/
error.rs

1//! 错误体系 — BaseException + 错误码映射
2//!
3//! 对齐 PHP `app\common\exception\BaseException`。
4//!
5//! ## PHP 错误码(从 PHP 后端代码提取)
6//!
7//! | code | 含义 | PHP 使用场景 |
8//! |------|------|-------------|
9//! | `1` | 成功 | `renderSuccess` 默认 |
10//! | `0` | 失败 | `renderError` 默认 / `BaseException` 默认 |
11//! | `-1` | 未登录/参数错误 | `not_login` / `缺少必要的参数` / `密钥不准确` |
12//! | `-2` | 用户不存在/未绑定 | `没有找到用户信息` / `请先绑定,员工信息` |
13//! | `-3` | 用户已禁用/已离职 | `员工信息待审核` / `您已离职` |
14//! | `403` | 无权限 |(Rust 扩展) |
15//! | `404` | 资源不存在 |(Rust 扩展) |
16//! | `422` | 验证失败 |(Rust 扩展) |
17//! | `500` | 数据库错误 |(Rust 扩展) |
18//!
19//! ## JSON 响应格式
20//!
21//! ```json
22//! { "code": <code>, "msg": "<msg>", "data": {} }
23//! ```
24
25use serde::Serialize;
26use thiserror::Error;
27
28/// 错误码枚举(对齐 PHP BaseException 的 code 字段)
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
30#[repr(i32)]
31pub enum ErrorCode {
32    /// 成功(PHP renderSuccess 默认)
33    Success = 1,
34    /// 失败(PHP renderError 默认 / BaseException 默认)
35    Failed = 0,
36    /// 未登录/参数错误(PHP not_login / 缺少必要的参数 / 密钥不准确)
37    NotLogin = -1,
38    /// 用户不存在/未绑定(PHP 没有找到用户信息 / 请先绑定,员工信息)
39    UserNotFound = -2,
40    /// 用户已禁用/已离职/待审核(PHP 员工信息待审核 / 您已离职)
41    UserDisabled = -3,
42    /// 无权限(Rust 扩展,HTTP 403)
43    Forbidden = 403,
44    /// 资源不存在(Rust 扩展,HTTP 404)
45    NotFound = 404,
46    /// 验证失败(Rust 扩展,HTTP 422)
47    ValidateFailed = 422,
48    /// 数据库错误(Rust 扩展,HTTP 500)
49    DbError = 500,
50}
51
52impl ErrorCode {
53    /// 转为 i32(对齐 PHP code 字段)
54    pub fn as_i32(self) -> i32 {
55        self as i32
56    }
57
58    /// 对应的 HTTP 状态码
59    pub fn http_status(self) -> u16 {
60        match self {
61            ErrorCode::Success => 200,
62            ErrorCode::Failed => 200,
63            ErrorCode::NotLogin => 401,
64            ErrorCode::UserNotFound => 401,
65            ErrorCode::UserDisabled => 403,
66            ErrorCode::Forbidden => 403,
67            ErrorCode::NotFound => 404,
68            ErrorCode::ValidateFailed => 422,
69            ErrorCode::DbError => 500,
70        }
71    }
72}
73
74impl From<i32> for ErrorCode {
75    fn from(code: i32) -> Self {
76        match code {
77            1 => ErrorCode::Success,
78            0 => ErrorCode::Failed,
79            -1 => ErrorCode::NotLogin,
80            -2 => ErrorCode::UserNotFound,
81            -3 => ErrorCode::UserDisabled,
82            403 => ErrorCode::Forbidden,
83            404 => ErrorCode::NotFound,
84            422 => ErrorCode::ValidateFailed,
85            500 => ErrorCode::DbError,
86            _ => ErrorCode::Failed,
87        }
88    }
89}
90
91/// BaseException — 对齐 PHP `app\common\exception\BaseException`
92///
93/// PHP 原始实现:
94/// ```php
95/// class BaseException extends Exception {
96///     public $code = 0;
97///     public $message = 'invalid parameters';
98///     public function __construct($params = []) {
99///         if (array_key_exists('code', $params)) { $this->code = $params['code']; }
100///         if (array_key_exists('msg', $params)) { $this->message = $params['msg']; }
101///     }
102/// }
103/// ```
104#[derive(Debug, Clone, Error)]
105#[error("[{code}] {msg}")]
106pub struct BaseException {
107    /// 错误码(对齐 PHP `$code`)
108    pub code: i32,
109    /// 错误消息(对齐 PHP `$message`,PHP 用 `msg` 键传入)
110    pub msg: String,
111}
112
113impl BaseException {
114    /// 创建 BaseException(对齐 PHP `new BaseException(['code' => x, 'msg' => y])`)
115    pub fn new(code: ErrorCode, msg: impl Into<String>) -> Self {
116        Self {
117            code: code.as_i32(),
118            msg: msg.into(),
119        }
120    }
121
122    /// 未登录快捷构造(对齐 PHP `throw new BaseException(['code' => -1, 'msg' => 'not_login'])`)
123    pub fn not_login(msg: impl Into<String>) -> Self {
124        Self::new(ErrorCode::NotLogin, msg)
125    }
126
127    /// 用户不存在快捷构造(对齐 PHP `throw new BaseException(['msg' => '没有找到用户信息', 'code' => -2])`)
128    pub fn user_not_found(msg: impl Into<String>) -> Self {
129        Self::new(ErrorCode::UserNotFound, msg)
130    }
131
132    /// 用户已禁用快捷构造(对齐 PHP `throw new BaseException(['msg' => '您已离职', 'code' => -3])`)
133    pub fn user_disabled(msg: impl Into<String>) -> Self {
134        Self::new(ErrorCode::UserDisabled, msg)
135    }
136
137    /// 失败快捷构造(对齐 PHP `renderError('error')`)
138    pub fn failed(msg: impl Into<String>) -> Self {
139        Self::new(ErrorCode::Failed, msg)
140    }
141
142    /// 无权限快捷构造
143    pub fn forbidden(msg: impl Into<String>) -> Self {
144        Self::new(ErrorCode::Forbidden, msg)
145    }
146
147    /// 资源不存在快捷构造
148    pub fn not_found(msg: impl Into<String>) -> Self {
149        Self::new(ErrorCode::NotFound, msg)
150    }
151
152    /// 验证失败快捷构造
153    pub fn validate_failed(msg: impl Into<String>) -> Self {
154        Self::new(ErrorCode::ValidateFailed, msg)
155    }
156
157    /// 数据库错误快捷构造
158    pub fn db_error(msg: impl Into<String>) -> Self {
159        Self::new(ErrorCode::DbError, msg)
160    }
161
162    /// 转为 JSON 响应(对齐 PHP `renderJson(code, msg, data)`)
163    pub fn to_json(&self) -> serde_json::Value {
164        serde_json::json!({
165            "code": self.code,
166            "msg": self.msg,
167            "data": {}
168        })
169    }
170}
171
172impl Default for BaseException {
173    fn default() -> Self {
174        Self {
175            code: ErrorCode::Failed.as_i32(),
176            msg: "invalid parameters".to_string(),
177        }
178    }
179}
180
181// ============================================================================
182// 单元测试
183// ============================================================================
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    /// 测试错误码值与 PHP 一一对应
190    #[test]
191    fn test_error_code_values() {
192        assert_eq!(ErrorCode::Success.as_i32(), 1);
193        assert_eq!(ErrorCode::Failed.as_i32(), 0);
194        assert_eq!(ErrorCode::NotLogin.as_i32(), -1);
195        assert_eq!(ErrorCode::UserNotFound.as_i32(), -2);
196        assert_eq!(ErrorCode::UserDisabled.as_i32(), -3);
197        assert_eq!(ErrorCode::Forbidden.as_i32(), 403);
198        assert_eq!(ErrorCode::NotFound.as_i32(), 404);
199        assert_eq!(ErrorCode::ValidateFailed.as_i32(), 422);
200        assert_eq!(ErrorCode::DbError.as_i32(), 500);
201    }
202
203    /// 测试 i32 → ErrorCode 转换
204    #[test]
205    fn test_from_i32() {
206        assert_eq!(ErrorCode::from(1), ErrorCode::Success);
207        assert_eq!(ErrorCode::from(0), ErrorCode::Failed);
208        assert_eq!(ErrorCode::from(-1), ErrorCode::NotLogin);
209        assert_eq!(ErrorCode::from(-2), ErrorCode::UserNotFound);
210        assert_eq!(ErrorCode::from(-3), ErrorCode::UserDisabled);
211        assert_eq!(ErrorCode::from(999), ErrorCode::Failed); // 未知码默认 Failed
212    }
213
214    /// 测试 HTTP 状态码映射
215    #[test]
216    fn test_http_status() {
217        assert_eq!(ErrorCode::Success.http_status(), 200);
218        assert_eq!(ErrorCode::Failed.http_status(), 200);
219        assert_eq!(ErrorCode::NotLogin.http_status(), 401);
220        assert_eq!(ErrorCode::UserNotFound.http_status(), 401);
221        assert_eq!(ErrorCode::UserDisabled.http_status(), 403);
222        assert_eq!(ErrorCode::Forbidden.http_status(), 403);
223        assert_eq!(ErrorCode::NotFound.http_status(), 404);
224        assert_eq!(ErrorCode::ValidateFailed.http_status(), 422);
225        assert_eq!(ErrorCode::DbError.http_status(), 500);
226    }
227
228    /// 测试 BaseException 默认值(对齐 PHP `code=0, message='invalid parameters'`)
229    #[test]
230    fn test_default() {
231        let ex = BaseException::default();
232        assert_eq!(ex.code, 0);
233        assert_eq!(ex.msg, "invalid parameters");
234    }
235
236    /// 测试 not_login 快捷构造(对齐 PHP `code=-1, msg='not_login'`)
237    #[test]
238    fn test_not_login() {
239        let ex = BaseException::not_login("not_login");
240        assert_eq!(ex.code, -1);
241        assert_eq!(ex.msg, "not_login");
242    }
243
244    /// 测试 user_not_found 快捷构造(对齐 PHP `code=-2, msg='没有找到用户信息'`)
245    #[test]
246    fn test_user_not_found() {
247        let ex = BaseException::user_not_found("没有找到用户信息");
248        assert_eq!(ex.code, -2);
249        assert_eq!(ex.msg, "没有找到用户信息");
250    }
251
252    /// 测试 user_disabled 快捷构造(对齐 PHP `code=-3, msg='您已离职'`)
253    #[test]
254    fn test_user_disabled() {
255        let ex = BaseException::user_disabled("您已离职,无权使用本系统!");
256        assert_eq!(ex.code, -3);
257        assert_eq!(ex.msg, "您已离职,无权使用本系统!");
258    }
259
260    /// 测试 failed 快捷构造(对齐 PHP `renderError('error')` → `code=0`)
261    #[test]
262    fn test_failed() {
263        let ex = BaseException::failed("操作失败");
264        assert_eq!(ex.code, 0);
265        assert_eq!(ex.msg, "操作失败");
266    }
267
268    /// 测试 to_json(对齐 PHP `renderJson(code, msg, data)`)
269    #[test]
270    fn test_to_json() {
271        let ex = BaseException::not_login("not_login");
272        let json = ex.to_json();
273        assert_eq!(json["code"], -1);
274        assert_eq!(json["msg"], "not_login");
275        assert_eq!(json["data"], serde_json::json!({}));
276    }
277
278    /// 测试 Display trait
279    #[test]
280    fn test_display() {
281        let ex = BaseException::not_login("not_login");
282        assert_eq!(format!("{}", ex), "[-1] not_login");
283    }
284
285    /// 测试从 PHP 场景提取的错误码全覆盖
286    /// PHP 代码中实际使用的错误码:1, 0, -1, -2, -3
287    #[test]
288    fn test_php_error_codes_coverage() {
289        // PHP renderSuccess → code=1
290        assert_eq!(ErrorCode::Success.as_i32(), 1);
291        // PHP renderError → code=0
292        assert_eq!(ErrorCode::Failed.as_i32(), 0);
293        // PHP not_login → code=-1
294        assert_eq!(ErrorCode::NotLogin.as_i32(), -1);
295        // PHP 没有找到用户信息 → code=-2
296        assert_eq!(ErrorCode::UserNotFound.as_i32(), -2);
297        // PHP 您已离职 → code=-3
298        assert_eq!(ErrorCode::UserDisabled.as_i32(), -3);
299    }
300}