diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py index 5b2745d7e..d7d8e0b1e 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/config_vo.py @@ -41,10 +41,15 @@ def get_config_name(self) -> str | None: def get_config_value(self) -> str | None: return self.config_value + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_config_key() self.get_config_name() self.get_config_value() + self.get_remark() class ConfigQueryModel(DateRangeQueryMixin, ConfigModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py index ec164c69d..61df92acf 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/dict_vo.py @@ -40,9 +40,14 @@ def get_dict_name(self) -> str | None: def get_dict_type(self) -> str | None: return self.dict_type + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_dict_name() self.get_dict_type() + self.get_remark() class DictDataModel(BaseModel): @@ -86,11 +91,16 @@ def get_dict_type(self) -> str | None: def get_css_class(self) -> str | None: return self.css_class + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_dict_label() self.get_dict_value() self.get_dict_type() self.get_css_class() + self.get_remark() class DictTypeQueryModel(DateRangeQueryMixin, DictTypeModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py index db77f30ce..329eefe54 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/job_vo.py @@ -112,9 +112,14 @@ def get_invoke_target(self) -> str | None: def get_cron_expression(self) -> str | None: return self.cron_expression + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_invoke_target() self.get_cron_expression() + self.get_remark() class JobPreviewRequest(BaseModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py index ccb63783d..80d427ca3 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/menu_vo.py @@ -61,6 +61,10 @@ def get_menu_type(self) -> Literal['M', 'C', 'F'] | None: def get_perms(self) -> str | None: return self.perms + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_menu_name() self.get_order_num() @@ -68,6 +72,7 @@ def validate_fields(self) -> None: self.get_component() self.get_menu_type() self.get_perms() + self.get_remark() class MenuQueryModel(DateRangeQueryMixin, MenuModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py index 055ffb54f..9fec9dd4a 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/notice_vo.py @@ -32,8 +32,13 @@ class NoticeModel(BaseModel): def get_notice_title(self) -> str | None: return self.notice_title + @Size(field_name='remark', min_length=0, max_length=255, message='备注长度不能超过255个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_notice_title() + self.get_remark() class NoticeQueryModel(DateRangeQueryMixin, NoticeModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py index 61b900bd9..e7ebfd5b4 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/post_vo.py @@ -40,10 +40,15 @@ def get_post_name(self) -> str | None: def get_post_sort(self) -> int | None: return self.post_sort + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_post_code() self.get_post_name() self.get_post_sort() + self.get_remark() class PostQueryModel(DateRangeQueryMixin, PostModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py index c711bc492..6c9906313 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/role_vo.py @@ -69,10 +69,15 @@ def get_role_key(self) -> str | None: def get_role_sort(self) -> int | None: return self.role_sort + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_role_name() self.get_role_key() self.get_role_sort() + self.get_remark() class RoleMenuModel(BaseModel): diff --git a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py index f3abc7155..d5bfc348c 100644 --- a/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py +++ b/ruoyi-fastapi-backend/module_admin/entity/vo/user_vo.py @@ -98,11 +98,16 @@ def get_email(self) -> str | None: def get_phonenumber(self) -> str | None: return self.phonenumber + @Size(field_name='remark', min_length=0, max_length=500, message='备注长度不能超过500个字符') + def get_remark(self) -> str | None: + return self.remark + def validate_fields(self) -> None: self.get_user_name() self.get_nick_name() self.get_email() self.get_phonenumber() + self.get_remark() class UserRowModel(UserModel): diff --git a/ruoyi-fastapi-backend/tests/module_admin/entity/test_vo_remark_length.py b/ruoyi-fastapi-backend/tests/module_admin/entity/test_vo_remark_length.py new file mode 100644 index 000000000..4d2582ef9 --- /dev/null +++ b/ruoyi-fastapi-backend/tests/module_admin/entity/test_vo_remark_length.py @@ -0,0 +1,66 @@ +"""备注字段长度校验测试。 + +备注列在各表中的真实长度来自 sql/ruoyi-fastapi-pg.sql: +sys_notice.remark 为 varchar(255),其余业务表的 remark 为 varchar(500)。 +超长备注在写库前必须被拦截,否则会在数据库层报错或(MySQL 非严格模式下)被静默截断。 +""" + +import pytest +from pydantic_validation_decorator import FieldValidationError + +from module_admin.entity.vo.config_vo import ConfigModel +from module_admin.entity.vo.dict_vo import DictDataModel, DictTypeModel +from module_admin.entity.vo.job_vo import JobModel +from module_admin.entity.vo.menu_vo import MenuModel +from module_admin.entity.vo.notice_vo import NoticeModel +from module_admin.entity.vo.post_vo import PostModel +from module_admin.entity.vo.role_vo import RoleModel +from module_admin.entity.vo.user_vo import UserModel + +# (模型, remark 列长度) +REMARK_LENGTH_CASES = [ + (UserModel, 500), + (RoleModel, 500), + (PostModel, 500), + (MenuModel, 500), + (ConfigModel, 500), + (DictTypeModel, 500), + (DictDataModel, 500), + (JobModel, 500), + (NoticeModel, 255), +] + + +class TestRemarkLengthValidation: + """备注字段长度校验。""" + + @pytest.mark.parametrize(('model', 'max_length'), REMARK_LENGTH_CASES) + def test_remark_at_the_column_limit_is_accepted(self, model: type, max_length: int) -> None: + instance = model(remark='备' * max_length) + + instance.get_remark() + + assert len(instance.remark) == max_length + + @pytest.mark.parametrize(('model', 'max_length'), REMARK_LENGTH_CASES) + def test_remark_one_past_the_column_limit_is_rejected(self, model: type, max_length: int) -> None: + instance = model(remark='备' * (max_length + 1)) + + with pytest.raises(FieldValidationError): + instance.get_remark() + + @pytest.mark.parametrize(('model', 'max_length'), REMARK_LENGTH_CASES) + def test_remark_is_optional(self, model: type, max_length: int) -> None: + instance = model(remark=None) + + instance.get_remark() + + assert instance.remark is None + + @pytest.mark.parametrize(('model', 'max_length'), REMARK_LENGTH_CASES) + def test_validate_fields_covers_remark(self, model: type, max_length: int) -> None: + """validate_fields 是服务层实际调用的入口,它必须覆盖备注字段。""" + instance = model(remark='备' * (max_length + 1)) + + with pytest.raises(FieldValidationError): + instance.validate_fields()