diff --git a/apps/constants/constants.py b/apps/constants/constants.py index e78eeef..1617ccb 100644 --- a/apps/constants/constants.py +++ b/apps/constants/constants.py @@ -1,205 +1,2 @@ -# +====================================================================== -# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] -# +---------------------------------------------------------------------- -# | Copyright (c) 2021-2026 一米阳光团队 版权所有 -# +---------------------------------------------------------------------- -# | 开源协议: Apache-2.0 -# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。 -# +---------------------------------------------------------------------- -# | 官方网站: https://www.djangoadmin.cn -# +---------------------------------------------------------------------- -# | 研发团队: 一米阳光团队 荣誉出品 -# +====================================================================== -# | 版权和免责声明: -# | 一、知识产权声明 -# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、 -# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权, -# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。 -# | 二、使用限制与禁止条款 -# | 您理解并同意,禁止将本产品用于以下任何行为: -# | 1. 任何违反中华人民共和国法律法规的用途; -# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为; -# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。 -# | 三、责任豁免 -# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失 -# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三 -# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。 -# | 四、违约与法律责任 -# | 如发现任何违反本声明的行为,本团队有权: -# | 1. 无条件配合公安机关及其他有权机关的调查取证工作; -# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失; -# | 3. 保留进一步采取刑事控告等法律措施的权利。 -# | 五、许可范围说明 -# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包 -# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通 -# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、 -# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造 -# | 成的一切经济损失及商誉损害。 -# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件 -# | 许可协议》附件为准。 -# +====================================================================== - -# 性别 -GENDER_LIST = { - 1: "男", - 2: "女", - 3: "保密", -} -# 部门类型 -DEPT_TYPE_LIST = { - 1: "公司", - 2: "子公司", - 3: "部门", - 4: "小组", -} - -# 站点类型 -ITEM_TYPE_LIST = { - 1: "普通站点", - 2: "其他站点" -} - -# 友链类型:1友情链接 2合作伙伴 -LINK_TYPE_LIST = { - 1: "友情链接", - 2: "合作伙伴" -} - -# 友链投放平台:1PC站 2WAP站 3微信小程序 4APP应用 -LINK_PLATFORM_LIST = { - 1: "PC站", - 2: "WAP站", - 3: "微信小程序", - 4: "APP应用" -} - -# 友链形式:1文字链接 2图片链接 -LINK_FORM_LIST = { - 1: "文字链接", - 2: "图片链接", -} - -# 广告投放平台:1PC站 2WAP站 3微信小程序 4APP应用 -AD_SORT_PLATFORM_LIST = { - 1: "PC站", - 2: "WAP站", - 3: "微信小程序", - 4: "APP应用" -} - -# 广告类型:1图片 2文字 3视频 4推荐 -AD_TYPE_LIST = { - 1: "图片", - 2: "文字", - 3: "视频", - 4: "推荐" -} - -# 通知来源:1官方平台 2开源中国 3CSDN官方 4新浪微博 -NOTICE_SOURCE_LIST = { - 1: "官方平台", - 2: "开源中国", - 3: "CSDN官方", - 4: "新浪微博", -} - -# 会员注册来源:1-网站注册 2-客户端注册 3-小程序注册 4-手机站注册 5-后台添加 -MEMBER_SOURCE_LIST = { - 1: "网站注册", - 2: "客户端注册", - 3: "小程序注册", - 4: "手机站注册", - 5: "后台添加" -} - -# 配置项类型 -CONFIG_DATA_TYPE_LIST = { - "text": "单行文本", - "textarea": "多行文本", - "ueditor": "富文本编辑器", - "date": "日期", - "datetime": "时间", - "number": "数字", - "select": "下拉框", - "radio": "单选框", - "checkbox": "复选框", - "image": "单张图片", - "images": "多张图片", - "password": "密码", - "icon": "字体图标", - "file": "单个文件", - "files": "多个文件", - "hidden": "隐藏", - "readonly": "只读文本", -} - -# 菜单类型:0-菜单 1-节点 -MENU_TYPE_LIST = { - 0: '菜单', - 1: '节点', -} - -# 打开方式:1-内部打开 2-外部打开 -MENU_TARGET_LIST = { - 1: '内部打开', - 2: '外部打开', -} - -# 城市级别 -CITY_LEVEL_LIST = { - 1: "省份", - 2: "城市", - 3: "县区", - 4: "街道" -} - -# 日志类型:1-登录,2-退出 -LOG_TYPE_LIST = { - 1: "登录", - 2: "退出", -} - -# 登录来源:0-后台,1-前台 -LOGIN_SOURCE_LIST = { - 0: "后台", - 1: "前台", -} - -# 登录状态:0-正常,1-异常 -LOGIN_STATUS_LIST = { - 0: "正常", - 1: "异常", -} - -# 操作日志来源:0-后台,1-前台 -OPERATION_SOURCE_LIST = { - 0: "后台", - 1: "前台", -} - -# 操作日志状态:0-正常,1-异常 -OPERATION_STATUS_LIST = { - 0: "正常", - 1: "异常", -} - -# 任务状态:0-未发布,1-运行中,2-暂停,3-删除 -JOB_STATUS_LIST = { - 0: "未发布", - 1: "运行中", - 2: "暂停", - 3: "删除", -} - -# 执行策略:1-立即执行,2-执行一次,3-放弃执行 -JOB_EXECUTE_POLICY_LIST = { - 1: "立即执行", - 2: "执行一次", - 3: "放弃执行", -} - -# 任务日志执行状态:0-正常 1-失败 -JOB_LOG_STATUS_LIST = { - 0: "正常", - 1: "失败", -} +# 兼容垫片:由 shared/enums.py 迁移,保留旧导入路径 +from shared.enums import * # noqa: F401,F403 diff --git a/apps/constants/message.py b/apps/constants/message.py index df85b4f..721d13c 100644 --- a/apps/constants/message.py +++ b/apps/constants/message.py @@ -1,85 +1,2 @@ -# +====================================================================== -# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] -# +---------------------------------------------------------------------- -# | Copyright (c) 2021-2026 珊瑚云团队 版权所有 -# +---------------------------------------------------------------------- -# | 开源协议: Apache-2.0 -# | 重要声明: 未经书面许可,不得移除或修改本框架相关版权标识。 -# +---------------------------------------------------------------------- -# | 官方网站: https://www.djangoadmin.cn -# +---------------------------------------------------------------------- -# | 研发团队: 珊瑚云团队 荣誉出品 -# +====================================================================== -# | 版权和免责声明: -# | 一、知识产权声明 -# | 本团队对该软件框架产品(以下简称“本产品”)拥有完整知识产权,包括但不限于商标权、专利权、 -# | 著作权、商业秘密等。前述权利均受中华人民共和国及相关国际法律法规保护。未经本团队书面授权, -# | 任何个人或单位不得基于本产品本身申请任何形式的知识产权。 -# | 二、使用限制与禁止条款 -# | 您理解并同意,禁止将本产品用于以下任何行为: -# | 1. 任何违反中华人民共和国法律法规的用途; -# | 2. 任何侵害他人合法权益(包括但不限于人身权、财产权、知识产权等)的行为; -# | 3. 任何危害国家安全、公共利益及社会道德风尚的行为。 -# | 三、责任豁免 -# | 因使用本产品而产生的任何损失或法律纠纷(包括但不限于直接损失、间接损失、附带损失、衍生损失 -# | 以及因违约、侵权等引发的责任),本团队均不承担任何法律责任。若因违规使用本产品导致任何第三 -# | 方提起索赔或行政机关进行调查,您应独立承担全部责任。 -# | 四、违约与法律责任 -# | 如发现任何违反本声明的行为,本团队有权: -# | 1. 无条件配合公安机关及其他有权机关的调查取证工作; -# | 2. 依法追究相关方的民事责任,并要求赔偿全部损失; -# | 3. 保留进一步采取刑事控告等法律措施的权利。 -# | 五、许可范围说明 -# | 1、被授权方应视本产品源代码为核心机密信息,负有最高标准的保密与保管义务。禁止以任何形式(包 -# | 括但不限于直接或间接的分享、复制、传播、分发、转售、出租、开源、反向工程、反编译、反汇编或通 -# | 过网络服务提供给任何第三方)泄露、披露或处置源代码的全部或部分内容。任何未经授权的复制、使用、 -# | 传播行为均构成对本团队知识产权的严重侵权,本团队有权依法追究其全部法律责任,并要求赔偿因此造 -# | 成的一切经济损失及商誉损害。 -# | 2、本产品仅限于公司或个人在法律允许范围内,用于内部且合规的软件产品研发。具体权利义务以《软件 -# | 许可协议》附件为准。 -# +====================================================================== - -# ======================= 分页配置 ========================== -# 该部分配置用于控制列表页面的数据分页显示 -# 通过调整每页记录数可以优化页面加载速度和用户体验 -# ========================================================== - -# 默认每页显示记录数 -# 类型:int -# 默认值:10 -# 说明:列表页面默认每页显示的数据条数,可根据实际需求调整 -PAGE_SIZE = 10 - -# ======================= 用户配置 ========================== -# 该部分配置用于管理用户账户相关的默认设置 -# 包含新用户创建和密码重置时的默认值 -# ========================================================== - -# 默认密码 -# 类型:str -# 默认值:"123456" -# 说明:新创建用户或重置密码时的默认密码,建议首次登录后强制修改 -DEFAULT_PASSWORD = "123456" - -# ======================= 消息提示配置 ========================== -# 该部分配置定义了所有操作响应的统一消息文本 -# 用于保持系统前后端消息提示的一致性 -# ========================================================== - -# 操作成功提示信息 -# 类型:str -# 默认值:"操作成功" -# 说明:统一的操作成功返回消息,所有成功操作均使用此消息 -MESSAGE_OK = "操作成功" - -# 操作失败提示信息 -# 类型:str -# 默认值:"操作失败" -# 说明:统一的操作失败返回消息,所有失败操作均使用此消息 -MESSAGE_FAIL = "操作失败" - -# ======================= 序列化安全配置 ========================== -# 模型序列化时统一排除的敏感字段,防止密码、盐值等泄露给前端 -# ================================================================== - -SERIALIZE_EXCLUDE_FIELDS = frozenset({'is_delete', 'password', 'salt'}) +# 兼容垫片:由 shared/constants.py 迁移,保留旧导入路径 +from shared.constants import * # noqa: F401,F403 diff --git a/shared/__init__.py b/shared/__init__.py new file mode 100644 index 0000000..5e93970 --- /dev/null +++ b/shared/__init__.py @@ -0,0 +1,17 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== + +"""跨模块共享层:枚举、常量、分页、模型基类、Repository 基类。""" + +from shared.constants import ( # noqa: F401 + PAGE_SIZE, + DEFAULT_PASSWORD, + MESSAGE_OK, + MESSAGE_FAIL, + SERIALIZE_EXCLUDE_FIELDS, +) +from shared.enums import * # noqa: F401,F403 diff --git a/shared/base_db.py b/shared/base_db.py new file mode 100644 index 0000000..aa6f26b --- /dev/null +++ b/shared/base_db.py @@ -0,0 +1,63 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== +# 由 apps/models/base_db.py 迁移而来。 + +""" +数据库操作基类模块 +============================================================= + +提供数据库模型的通用操作方法,包括: +- 单条数据保存(save):add + flush,不提交事务 +- 多条数据批量保存(save_all):add_all + flush,不提交事务 +- 数据删除(delete):delete + flush,不提交事务 + +事务边界由 db_session_middleware 统一管理: +- 请求成功且为写操作 → 自动 commit +- 请求异常 → 自动 rollback +- 业务代码无需关心事务提交 +""" + +import logging + +from core.database import get_db_session + +logger = logging.getLogger(__name__) + + +class base_db: + __abstract__ = True + + def save(self): + """ + 保存当前实例到数据库 + + 将对象添加到会话并 flush(发送 SQL 到数据库获取自增 ID), + 但不提交事务。事务由中间件统一管理。 + + Returns: + self: 当前实例,支持链式调用。通过 self.id 可获取自增主键。 + """ + db = get_db_session() + db.add(self) + db.flush() + return self + + @staticmethod + def save_all(*args): + """批量保存多个实例到数据库""" + if not args: + return + db = get_db_session() + db.add_all(args) + db.flush() + + def delete(self): + """从数据库删除当前实例""" + db = get_db_session() + db.delete(self) + db.flush() + return True diff --git a/shared/base_model.py b/shared/base_model.py new file mode 100644 index 0000000..e3a5539 --- /dev/null +++ b/shared/base_model.py @@ -0,0 +1,149 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== +# 由 apps/models/base_model.py 迁移而来。 + +"""数据库模型基类:通用字段、序列化与通用查询方法。""" + +import logging +from datetime import date, datetime, timezone +from typing import Optional, List, Dict, Any + +from sqlalchemy import Column, Integer, DATETIME, and_ + +from core import R +from core.database import Base, get_db_session +from shared.constants import SERIALIZE_EXCLUDE_FIELDS +from utils.request import parse_id_list + +logger = logging.getLogger(__name__) + +_UTC_NOW = lambda: datetime.now(tz=timezone.utc) + + +class base_model(Base): + """ + 数据库模型基类 + + 所有业务模型应继承此类,自动获得通用字段和公共方法。 + + 提供的功能: + 1. 通用字段:id、创建人、创建时间、更新人、更新时间、软删除标识 + 2. 序列化方法:to_dict() + 3. 查询方法:get_by_id()、get_by_ids()、get_all_active()、exists() + 4. 删除方法:batch_delete_r() + """ + + __abstract__ = True + + # ============================================================ + # 通用字段定义 + # ============================================================ + + id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID") + create_user = Column(Integer, default=0, comment="创建人") + create_time = Column(DATETIME, default=_UTC_NOW, comment="创建时间") + update_user = Column(Integer, default=0, comment="更新人") + update_time = Column(DATETIME, default=_UTC_NOW, onupdate=_UTC_NOW, comment="更新时间") + is_delete = Column(Integer, default=0, comment="删除标识:0-正常 1-已删除") + + # ============================================================ + # 序列化方法 + # ============================================================ + + def to_dict(self) -> Dict[str, Any]: + """ + 将模型实例转换为字典 + + 自动排除敏感字段(is_delete、password、salt)。 + datetime 类型格式化为字符串,UTC 时间转为本地时间便于前端展示。 + """ + result = {} + for column in self.__table__.columns: + if column.name in SERIALIZE_EXCLUDE_FIELDS: + continue + value = getattr(self, column.name) + if isinstance(value, datetime): + if value.tzinfo is not None: + # UTC 时间转为本地时间 + value = value.astimezone() + value = value.strftime('%Y-%m-%d %H:%M:%S') + elif isinstance(value, date): + # 纯日期类型(如生日)无时分秒,单独格式化,避免 JSON 序列化失败 + value = value.strftime('%Y-%m-%d') + result[column.name] = value + return result + + # ============================================================ + # 通用查询方法 + # ============================================================ + + @classmethod + def get_by_id(cls, id: Optional[int]) -> Optional['base_model']: + """根据ID获取未删除的记录,不存在返回 None""" + if id is None or id <= 0: + return None + db = get_db_session() + return db.query(cls).filter(cls.id == id, cls.is_delete == 0).first() + + @classmethod + def get_by_ids(cls, ids: List[int]) -> List['base_model']: + """根据ID列表批量获取未删除的记录""" + if not ids: + return [] + valid_ids = [i for i in ids if i and i > 0] + if not valid_ids: + return [] + db = get_db_session() + return db.query(cls).filter(cls.id.in_(valid_ids), cls.is_delete == 0).all() + + @classmethod + def get_all_active(cls) -> List['base_model']: + """获取所有未删除的记录""" + db = get_db_session() + return db.query(cls).filter(cls.is_delete == 0).all() + + @classmethod + def exists(cls, id: Optional[int]) -> bool: + """检查未删除记录是否存在""" + if id is None or id <= 0: + return False + db = get_db_session() + return db.query(cls).filter(cls.id == id, cls.is_delete == 0).count() > 0 + + # ============================================================ + # 删除方法 + # ============================================================ + + @classmethod + def batch_delete_r(cls, ids_str: Optional[str]): + """ + 批量软删除并返回R对象 + + 封装ID校验、解析、软删除的完整流程。 + + Args: + ids_str: 逗号分隔的ID字符串,如 "1,2,3" + + Returns: + R: 操作结果 + """ + if not ids_str: + return R.failed("记录ID不存在") + + id_list = parse_id_list(ids_str) + if not id_list: + return R.failed("记录ID不存在") + + db = get_db_session() + records = db.query(cls).filter(and_(cls.id.in_(id_list), cls.is_delete == 0)).all() + if len(records) != len(id_list): + return R.failed("记录不存在") + + for record in records: + record.is_delete = 1 + + return R.ok(msg="本次共删除{0}条数据".format(len(records))) diff --git a/shared/base_repository.py b/shared/base_repository.py new file mode 100644 index 0000000..3de24cc --- /dev/null +++ b/shared/base_repository.py @@ -0,0 +1,134 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== + +"""通用 Repository 基类:封装各模型的数据访问层公共操作。 + +各模块 repository.py 继承本类并提供自定义查询,service 通过 repository 访问数据库。 +过渡期与 base_model 的 classmethod 双轨并存,全部模块迁移完成后收敛。 +""" + +from typing import Generic, Optional, TypeVar + +from core.database import get_db_session +from shared.constants import PAGE_SIZE +from utils.request import parse_id_list + +M = TypeVar("M") + + +class BaseRepository(Generic[M]): + """模型数据访问基类。""" + + # 软删除列名,JobLogRepository 覆盖为 "del_flag" + soft_delete_col: str = "is_delete" + + def __init__(self, model_cls): + self.model = model_cls + + @property + def db(self): + """当前请求的数据库会话。""" + return get_db_session() + + def _soft_col(self): + return getattr(self.model, self.soft_delete_col) + + # ============================================================ + # 基础查询 + # ============================================================ + + def get_by_id(self, obj_id: Optional[int]) -> Optional[M]: + """根据ID获取未删除记录。""" + if obj_id is None or obj_id <= 0: + return None + return self.db.query(self.model).filter( + self.model.id == obj_id, self._soft_col() == 0 + ).first() + + def get_by_ids(self, ids) -> list: + """根据ID列表批量获取未删除记录。""" + valid = [i for i in ids if i and i > 0] + if not valid: + return [] + return self.db.query(self.model).filter( + self.model.id.in_(valid), self._soft_col() == 0 + ).all() + + def get_all_active(self) -> list: + """获取所有未删除记录。""" + return self.db.query(self.model).filter(self._soft_col() == 0).all() + + def exists(self, obj_id: Optional[int]) -> bool: + """检查未删除记录是否存在。""" + return self.get_by_id(obj_id) is not None + + # ============================================================ + # 分页查询 + # ============================================================ + + def paginate(self, page_no: int = 1, page_size: int = PAGE_SIZE, query=None): + """ + 分页查询,返回 (items, total)。自动追加软删过滤。 + 传入 query 可在外部先构建过滤条件。 + """ + q = query if query is not None else self.db.query(self.model) + q = q.filter(self._soft_col() == 0) + total = q.count() + items = q.limit(page_size).offset((page_no - 1) * page_size).all() + return items, total + + # ============================================================ + # 写操作 + # ============================================================ + + def create(self, **fields) -> M: + """创建记录(不提交事务,由中间件统一管理)。""" + obj = self.model(**fields) + obj.save() + return obj + + def update(self, obj_id, data: dict) -> int: + """按ID更新,返回受影响行数。""" + return self.db.query(self.model).filter( + self.model.id == obj_id, self._soft_col() == 0 + ).update(data) + + def soft_delete(self, ids) -> bool: + """ + 批量软删除(等价 base_model.batch_delete_r 的语义)。 + + Args: + ids: 逗号分隔字符串或整数列表 + + Returns: + bool: 是否全部删除成功(ID 数需与匹配记录数一致) + """ + id_list = parse_id_list(ids) if isinstance(ids, str) else [i for i in ids if i] + if not id_list: + return False + records = self.db.query(self.model).filter( + self.model.id.in_(id_list), self._soft_col() == 0 + ).all() + if len(records) != len(id_list): + return False + for record in records: + setattr(record, self.soft_delete_col, 1) + return True + + def hard_delete(self, obj) -> bool: + """物理删除实例。""" + self.db.delete(obj) + self.db.flush() + return True + + # ============================================================ + # 自定义查询出口 + # ============================================================ + + def filter(self, *criterion): + """返回过滤后的 query,供子类组合复杂查询。""" + return self.db.query(self.model).filter(*criterion) diff --git a/shared/base_service.py b/shared/base_service.py new file mode 100644 index 0000000..0a89762 --- /dev/null +++ b/shared/base_service.py @@ -0,0 +1,30 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== + +"""Service 基类(可选):为面向对象的 service 提供 repository 与响应便捷属性。 + +本项目现有 service 为模块级函数风格,如后续需要类风格 service 可继承本类。 +""" + +from core import response as R + + +class BaseService: + """Service 基类:绑定 repository 与统一响应助手。""" + + repo = None + + def __init__(self, repo=None): + self.repo = repo or self.repo + + @property + def ok(self): + return R.ok + + @property + def failed(self): + return R.failed diff --git a/shared/constants.py b/shared/constants.py new file mode 100644 index 0000000..7b1ec0d --- /dev/null +++ b/shared/constants.py @@ -0,0 +1,28 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== +# 由 apps/constants/message.py 迁移而来。 + +"""全局常量:分页、默认密码、消息文本、序列化排除字段。""" + +# ======================= 分页配置 ========================== +# 默认每页显示记录数 +PAGE_SIZE = 10 + +# ======================= 用户配置 ========================== +# 默认密码(新创建用户或重置密码时的默认密码,建议首次登录后强制修改) +DEFAULT_PASSWORD = "123456" + +# ======================= 消息提示配置 ========================== +# 操作成功提示信息 +MESSAGE_OK = "操作成功" + +# 操作失败提示信息 +MESSAGE_FAIL = "操作失败" + +# ======================= 序列化安全配置 ========================== +# 模型序列化时统一排除的敏感字段,防止密码、盐值等泄露给前端 +SERIALIZE_EXCLUDE_FIELDS = frozenset({'is_delete', 'password', 'salt'}) diff --git a/shared/enums.py b/shared/enums.py new file mode 100644 index 0000000..20a92a3 --- /dev/null +++ b/shared/enums.py @@ -0,0 +1,174 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== +# 由 apps/constants/constants.py 迁移而来。 + +"""业务枚举字典:各模块的状态/类型展示映射。""" + +# 性别 +GENDER_LIST = { + 1: "男", + 2: "女", + 3: "保密", +} +# 部门类型 +DEPT_TYPE_LIST = { + 1: "公司", + 2: "子公司", + 3: "部门", + 4: "小组", +} + +# 站点类型 +ITEM_TYPE_LIST = { + 1: "普通站点", + 2: "其他站点" +} + +# 友链类型:1友情链接 2合作伙伴 +LINK_TYPE_LIST = { + 1: "友情链接", + 2: "合作伙伴" +} + +# 友链投放平台:1PC站 2WAP站 3微信小程序 4APP应用 +LINK_PLATFORM_LIST = { + 1: "PC站", + 2: "WAP站", + 3: "微信小程序", + 4: "APP应用" +} + +# 友链形式:1文字链接 2图片链接 +LINK_FORM_LIST = { + 1: "文字链接", + 2: "图片链接", +} + +# 广告投放平台:1PC站 2WAP站 3微信小程序 4APP应用 +AD_SORT_PLATFORM_LIST = { + 1: "PC站", + 2: "WAP站", + 3: "微信小程序", + 4: "APP应用" +} + +# 广告类型:1图片 2文字 3视频 4推荐 +AD_TYPE_LIST = { + 1: "图片", + 2: "文字", + 3: "视频", + 4: "推荐" +} + +# 通知来源:1官方平台 2开源中国 3CSDN官方 4新浪微博 +NOTICE_SOURCE_LIST = { + 1: "官方平台", + 2: "开源中国", + 3: "CSDN官方", + 4: "新浪微博", +} + +# 会员注册来源:1-网站注册 2-客户端注册 3-小程序注册 4-手机站注册 5-后台添加 +MEMBER_SOURCE_LIST = { + 1: "网站注册", + 2: "客户端注册", + 3: "小程序注册", + 4: "手机站注册", + 5: "后台添加" +} + +# 配置项类型 +CONFIG_DATA_TYPE_LIST = { + "text": "单行文本", + "textarea": "多行文本", + "ueditor": "富文本编辑器", + "date": "日期", + "datetime": "时间", + "number": "数字", + "select": "下拉框", + "radio": "单选框", + "checkbox": "复选框", + "image": "单张图片", + "images": "多张图片", + "password": "密码", + "icon": "字体图标", + "file": "单个文件", + "files": "多个文件", + "hidden": "隐藏", + "readonly": "只读文本", +} + +# 菜单类型:0-菜单 1-节点 +MENU_TYPE_LIST = { + 0: '菜单', + 1: '节点', +} + +# 打开方式:1-内部打开 2-外部打开 +MENU_TARGET_LIST = { + 1: '内部打开', + 2: '外部打开', +} + +# 城市级别 +CITY_LEVEL_LIST = { + 1: "省份", + 2: "城市", + 3: "县区", + 4: "街道" +} + +# 日志类型:1-登录,2-退出 +LOG_TYPE_LIST = { + 1: "登录", + 2: "退出", +} + +# 登录来源:0-后台,1-前台 +LOGIN_SOURCE_LIST = { + 0: "后台", + 1: "前台", +} + +# 登录状态:0-正常,1-异常 +LOGIN_STATUS_LIST = { + 0: "正常", + 1: "异常", +} + +# 操作日志来源:0-后台,1-前台 +OPERATION_SOURCE_LIST = { + 0: "后台", + 1: "前台", +} + +# 操作日志状态:0-正常,1-异常 +OPERATION_STATUS_LIST = { + 0: "正常", + 1: "异常", +} + +# 任务状态:0-未发布,1-运行中,2-暂停,3-删除 +JOB_STATUS_LIST = { + 0: "未发布", + 1: "运行中", + 2: "暂停", + 3: "删除", +} + +# 执行策略:1-立即执行,2-执行一次,3-放弃执行 +JOB_EXECUTE_POLICY_LIST = { + 1: "立即执行", + 2: "执行一次", + 3: "放弃执行", +} + +# 任务日志执行状态:0-正常 1-失败 +JOB_LOG_STATUS_LIST = { + 0: "正常", + 1: "失败", +} diff --git a/shared/pagination.py b/shared/pagination.py new file mode 100644 index 0000000..88510f1 --- /dev/null +++ b/shared/pagination.py @@ -0,0 +1,54 @@ +# +====================================================================== +# | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] +# +---------------------------------------------------------------------- +# | Copyright (c) 2021-2026 一米阳光团队 版权所有 +# | 开源协议: Apache-2.0 +# +====================================================================== + +"""分页统一封装:解析 pageNo/pageSize 并对 SQLAlchemy query 应用分页。""" + +from shared.constants import PAGE_SIZE + + +def get_page_params(query_params, default_size: int = PAGE_SIZE) -> tuple[int, int]: + """ + 从 request.query_params 解析分页参数。 + + Args: + query_params: request.query_params(类 dict 对象) + default_size: 默认每页条数 + + Returns: + (page, size):page>=1,size>=1 + """ + try: + page = int(query_params.get('pageNo', 1) or 1) + except (TypeError, ValueError): + page = 1 + try: + size = int(query_params.get('pageSize', default_size) or default_size) + except (TypeError, ValueError): + size = default_size + + if page < 1: + page = 1 + if size < 1: + size = default_size + return page, size + + +def paginate_query(query, page: int, size: int) -> tuple[list, int]: + """ + 对 SQLAlchemy query 应用分页并返回 (items, total)。 + + Args: + query: 已构建的 SQLAlchemy Query 对象(不含 count/limit/offset) + page: 页码(>=1) + size: 每页条数 + + Returns: + (items, total):当前页数据列表与总记录数 + """ + total = query.count() + items = query.limit(size).offset((page - 1) * size).all() + return items, total diff --git a/tests/unit/core/test_pagination.py b/tests/unit/core/test_pagination.py new file mode 100644 index 0000000..5410377 --- /dev/null +++ b/tests/unit/core/test_pagination.py @@ -0,0 +1,21 @@ +"""shared/pagination.py 分页工具单元测试。""" + +from shared.pagination import get_page_params + + +def test_defaults(): + page, size = get_page_params({}) + assert page == 1 + assert size == 10 + + +def test_valid_params(): + page, size = get_page_params({"pageNo": "3", "pageSize": "20"}) + assert page == 3 + assert size == 20 + + +def test_invalid_params_fall_back(): + page, size = get_page_params({"pageNo": "abc", "pageSize": "0"}) + assert page == 1 + assert size == 10