init: 初始化 dpb 桃育种系统代码库

前后端 + 后端 FastAPI 全量源码、部署脚本与文档。
This commit is contained in:
34047007@qq.com
2026-08-06 00:17:49 +08:00
commit b95053c52c
1469 changed files with 322298 additions and 0 deletions
@@ -0,0 +1 @@
@@ -0,0 +1,36 @@
from typing import Annotated
from fastapi import APIRouter, Body, Depends, Path, Security
from fastapi.responses import JSONResponse
from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from app.common.response import ResponseSchema, SuccessResponse
from app.core.base_schema import AuthSchema
from app.core.dependencies import AuthPermission, db_getter, redis_getter
from app.core.router_class import OperationLogRoute
from .schema import ParamsOutSchema, ParamsUpdateSchema
from .service import ParamsService
ParamsRouter = APIRouter(route_class=OperationLogRoute, prefix="/param", tags=["参数管理"])
@ParamsRouter.put("/update/{id}", summary="修改参数", response_model=ResponseSchema[ParamsOutSchema])
async def update_param_controller(
redis: Annotated[Redis, Depends(redis_getter)],
auth: Annotated[AuthSchema, Security(AuthPermission(["module_system:param:update"]))],
db: Annotated[AsyncSession, Depends(db_getter)],
id: Annotated[int, Path(description="参数ID")],
data: Annotated[ParamsUpdateSchema, Body(description="参数修改参数")],
) -> JSONResponse:
result_dict = await ParamsService(auth, db).update(redis=redis, id=id, data=data)
return SuccessResponse(data=result_dict, msg="更新参数成功")
@ParamsRouter.get("/info", summary="获取初始化缓存参数", response_model=ResponseSchema[list[ParamsOutSchema]])
async def get_init_config_controller(
redis: Annotated[Redis, Depends(redis_getter)],
) -> JSONResponse:
result_dict = await ParamsService.get_init_cache(redis=redis)
return SuccessResponse(data=result_dict, msg="获取初始化缓存参数成功")
@@ -0,0 +1,23 @@
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.base_crud import CRUDBase
from app.core.base_schema import AuthSchema
from .model import ParamsModel
from .schema import ParamsUpdateSchema
class ParamsCRUD(CRUDBase[ParamsModel, ParamsUpdateSchema, ParamsUpdateSchema]):
"""配置管理数据层"""
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
"""初始化系统参数配置数据层。
参数:
- auth (AuthSchema): 认证信息模型(含 DB 会话等上下文)。
- db (AsyncSession): 数据库会话。
返回:
- None
"""
super().__init__(model=ParamsModel, auth=auth, db=db)
@@ -0,0 +1,18 @@
from sqlalchemy import Boolean, Integer, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.base_model import ModelMixin
class ParamsModel(ModelMixin):
"""系统参数表"""
__tablename__: str = "sys_param"
__table_args__: dict[str, str] = {"comment": "系统参数表"}
config_name: Mapped[str] = mapped_column(String(64), nullable=False, index=True, comment="参数名称")
config_key: Mapped[str] = mapped_column(String(500), nullable=False, index=True, comment="参数键名")
config_value: Mapped[str | None] = mapped_column(Text, comment="参数键值")
config_type: Mapped[bool] = mapped_column(Boolean, default=False, nullable=True, comment="系统内置(True:是 False:否)", index=True)
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)")
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
@@ -0,0 +1,46 @@
import re
from pydantic import BaseModel, ConfigDict, Field, field_validator
from app.core.base_schema import BaseSchema
class ParamsBaseSchema(BaseModel):
"""参数基础字段
"""
config_name: str = Field(..., min_length=1, max_length=64, description="参数名称")
config_key: str = Field(..., min_length=1, max_length=500, description="参数键名(小写字母开头,仅允许字母数字_.-)")
config_value: str | None = Field(default=None, description="参数键值")
config_type: bool = Field(default=False, description="是否系统内置(True:是 False:否)")
status: int = Field(default=0, ge=0, le=1, description="状态(0:正常 1:停用)")
description: str | None = Field(default=None, max_length=500, description="参数描述")
@field_validator("config_key")
@classmethod
def _validate_config_key(cls, v: str) -> str:
"""校验参数键名:小写字母开头,仅含字母/数字/_ . -"""
v = v.strip().lower()
if not re.match(r"^[a-z][a-z0-9_.-]*$", v):
raise ValueError("参数键名必须以小写字母开头,仅允许小写字母、数字、_ . -")
return v
@field_validator("status")
@classmethod
def _validate_status(cls, v: int) -> int:
"""校验状态:仅支持 0(正常) 或 1(停用)"""
if v not in {0, 1}:
raise ValueError("状态仅支持 0(正常) 或 1(停用)")
return v
class ParamsUpdateSchema(ParamsBaseSchema):
"""参数更新模型
"""
class ParamsOutSchema(ParamsBaseSchema, BaseSchema):
"""参数响应模型
"""
model_config = ConfigDict(from_attributes=True)
@@ -0,0 +1,118 @@
import json
from collections.abc import Sequence
from redis.asyncio.client import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from app.common.enums import RedisInitKeyConfig
from app.core.base_schema import AuthSchema
from app.core.database import async_db_session
from app.core.exceptions import CustomException
from app.core.logger import logger
from app.core.redis_crud import RedisCURD
from .crud import ParamsCRUD
from .schema import ParamsOutSchema, ParamsUpdateSchema
class ParamsService:
"""参数管理服务"""
def __init__(self, auth: AuthSchema, db: AsyncSession) -> None:
self.auth = auth
self.db = db
async def update(self, redis: Redis, id: int, data: ParamsUpdateSchema) -> ParamsOutSchema:
"""更新参数
参数:
- redis (Redis): Redis 客户端实例
- id (int): 参数ID
- data (ParamsUpdateSchema): 参数更新模型
返回:
- ParamsOutSchema: 更新后的参数响应模型
"""
exist_obj = await ParamsCRUD(self.auth, self.db).get_or_404(id=id, msg="更新失败,该数据不存在")
if exist_obj.config_key != data.config_key:
raise CustomException(msg="更新失败,系统配置key不允许修改")
new_obj = await ParamsCRUD(self.auth, self.db).update(id=id, data=data)
if not new_obj:
raise CustomException(msg="更新失败,系统配置不存在")
out = ParamsOutSchema.model_validate(new_obj)
redis_payload = out.model_dump(mode="json")
# 同步redis
user = self.auth.user
if not user:
raise CustomException(msg="未登录")
redis_key = f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:{new_obj.config_key}"
try:
value = json.dumps(redis_payload, ensure_ascii=False)
result = await RedisCURD(redis).set(
key=redis_key,
value=value,
expire=None,
)
if not result:
logger.error(f"同步配置到缓存失败: {out}")
raise CustomException(msg="同步配置到缓存失败")
except Exception as e:
logger.error(f"更新系统配置失败: {e}")
raise CustomException(msg="同步配置到缓存失败") from e
return out
@staticmethod
async def _load_all_configs_from_db() -> Sequence[object]:
async with async_db_session() as session, session.begin():
init_auth = AuthSchema()
return await ParamsCRUD(init_auth, session).get_list()
@staticmethod
async def _sync_configs_to_redis(redis: Redis, config_obj: Sequence) -> list[dict]:
"""将 DB 配置写入 Redis,返回对应的 dict 列表。"""
configs: list[dict] = []
for config in config_obj:
redis_key = f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:{config.config_key}"
out = ParamsOutSchema.model_validate(config)
payload = out.model_dump(mode="json")
try:
await RedisCURD(redis).set(redis_key, json.dumps(payload, ensure_ascii=False))
configs.append(out.model_dump())
except Exception as e:
logger.error(f"❌️ 缓存系统配置失败: {redis_key}: {e}")
return configs
@staticmethod
async def init_cache(redis: Redis) -> None:
"""启动时初始化系统参数到 Redis。"""
try:
config_obj = await ParamsService._load_all_configs_from_db()
if not config_obj:
raise CustomException(msg="该数据不存在")
await ParamsService._sync_configs_to_redis(redis, config_obj)
except Exception as e:
logger.error(f"❌️ 初始化系统参数到 Redis 失败: {e}")
raise CustomException(msg="初始化系统参数到 Redis 失败") from e
@staticmethod
async def get_init_cache(redis: Redis) -> list[dict]:
"""从 Redis 读取系统配置;为空时自动回源 DB。"""
redis_keys = await RedisCURD(redis).get_keys(f"{RedisInitKeyConfig.SYSTEM_CONFIG.key}:*")
redis_configs = await RedisCURD(redis).mget(redis_keys)
configs = []
for raw in redis_configs:
if not raw:
continue
try:
configs.append(json.loads(raw))
except Exception as e:
logger.error(f"解析系统配置数据失败: {e}")
if not configs:
config_obj = await ParamsService._load_all_configs_from_db()
if config_obj:
configs = await ParamsService._sync_configs_to_redis(redis, config_obj)
return configs