上传规范文档

This commit is contained in:
2025-11-17 16:39:30 +08:00
parent 545335b666
commit 638e152cff
11 changed files with 2757 additions and 0 deletions

158
docs/开发规范.md Normal file
View File

@@ -0,0 +1,158 @@
# 新功能开发规范
## 一、命名规范
- APP 名称:`hertz_studio_django_xxx`,全小写,用下划线分隔
- Python 包与模块:全小写,短名称,避免缩写不清晰
- URL 命名空间:`app_name = 'hertz_studio_django_xxx'`
- 数据库表名:`Meta.db_table = 'hertz_xxx_model'`,避免与其他库冲突
- 迁移文件命名:描述性动词+对象,如 `0003_add_field_to_model`
## 二、项目结构与约定
- 标准结构:`apps.py``models.py``serializers.py``views.py``urls.py``admin.py`
- 静态资源:`media/<app_name>/...` 放置同路径资源以覆盖库静态文件
- 配置集中:在 `settings.py` 维护,使用前缀化大写变量(如 `YOLO_MODEL`
## 三、接口返回规范
- 统一使用 `HertzResponse`(路径:`hertz_studio_django_utils/responses/HertzResponse.py`
- 成功:
```python
from hertz_studio_django_utils.responses.HertzResponse import HertzResponse
return HertzResponse.success(data={'id': 1}, message='操作成功')
```
- 失败:
```python
return HertzResponse.fail(message='业务失败')
```
- 错误:
```python
return HertzResponse.error(message='系统错误', error=str(e))
```
- 验证失败:
```python
return HertzResponse.validation_error(message='参数验证失败', errors=serializer.errors)
```
- 统一键:`success | code | message | data`,禁止返回非标准顶层结构
## 四、API 设计规范
- 路径语义化:`/models/`、`/detections/`、`/alerts/`
- 方法约定:`GET` 查询、`POST` 创建/动作、`PUT/PATCH` 更新、`DELETE` 删除
- 分页:请求参数 `page, page_size`;响应 `total, items`
- 过滤与排序:查询参数 `q, order_by, filters`;谨慎开放可排序字段
## 五、认证与授权
- 强制认证:业务敏感接口使用登录态(装饰器或 DRF 权限类)
- 权限控制:按用户/组/角色配置;避免在视图中硬编码权限
- 速率限制:对登录、验证码、检测等接口进行限流
## 六、日志与审计
- 请求审计:记录请求方法、路径、用户、响应码、耗时
- 业务事件:模型启用/删除、检测结果、告警变更记录
- 脱敏:对密码、令牌、隐私字段进行统一脱敏
## 七、配置约定
- 所有库配置集中在 `settings.py`,使用前缀化变量:
- AI`AI_MODEL_PROVIDER`、`AI_DEFAULT_MODEL`、`AI_TIMEOUT`
- Auth`AUTH_LOGIN_REDIRECT_URL`、`AUTH_ENABLE_OAUTH`
- Captcha`CAPTCHA_TYPE`、`CAPTCHA_EXPIRE_SECONDS`
- Log`LOG_LEVEL`、`LOG_SINKS`、`LOG_REDACT_FIELDS`
- Notice`NOTICE_CHANNELS`、`NOTICE_RETRY`
- Monitor`MONITOR_PROBES`、`MONITOR_ALERTS`
- Wiki`WIKI_MARKDOWN`、`WIKI_SEARCH_BACKEND`
- YOLO`YOLO_MODEL`、`YOLO_DEVICE`、`YOLO_CONF_THRESHOLD`
- Codegen`CODEGEN_TEMPLATES_DIR`、`CODEGEN_OUTPUT_DIR`
## 八、可扩展性(不改库源码)
- 视图子类化 + 路由覆盖:在项目中继承库视图并替换路由匹配
```python
# urls.py
from django.urls import path, include
from your_app.views import MyDetectView
urlpatterns = [
path('yolo/detect/', MyDetectView.as_view(), name='detect'),
path('yolo/', include('hertz_studio_django_yolo.urls', namespace='hertz_studio_django_yolo')),
]
```
- 猴子补丁:在 `AppConfig.ready()` 将库函数替换为自定义函数
```python
# apps.py
from django.apps import AppConfig
class YourAppConfig(AppConfig):
name = 'your_app'
def ready(self):
from hertz_studio_django_yolo import views as yviews
from your_app.views import my_yolo_detection
yviews.yolo_detection = my_yolo_detection
```
- Admin 重注册:`unregister` 后 `register` 自定义 `ModelAdmin`
- 信号连接:在 `ready()` 中连接库暴露的信号以扩展行为
## 九、示例:覆写 YOLO 检测返回值
- 目标位置:`hertz_studio_django_yolo/views.py:586-603`
- 最小替换示例(路由覆盖):
```python
from rest_framework.decorators import api_view, parser_classes
from rest_framework.parsers import MultiPartParser, FormParser
from django.contrib.auth.decorators import login_required
from hertz_studio_django_utils.responses.HertzResponse import HertzResponse
from hertz_studio_django_yolo.views import _perform_detection
from hertz_studio_django_yolo.models import YoloModel, DetectionRecord
import uuid, os, time, shutil
@api_view(['POST'])
@parser_classes([MultiPartParser, FormParser])
@login_required
def my_yolo_detection(request):
# 复用库的流程,省略若干步骤,仅演示返回体差异化
serializer = hertz_studio_django_yolo.serializers.DetectionRequestSerializer(data=request.data)
if not serializer.is_valid():
return HertzResponse.validation_error(message='参数验证失败', errors=serializer.errors)
uploaded_file = serializer.validated_data['file']
yolo_model = YoloModel.get_enabled_model()
original_path = '...' # 省略:存储原始文件
result_path, object_count, detected_categories, confidence_scores, avg_confidence = _perform_detection('...', yolo_model.model_path, 0.5, 'image', yolo_model)
processing_time = time.time() - time.time()
detection_record = DetectionRecord.objects.create(
original_file=original_path,
result_file='...',
detection_type='image',
model_name=f"{yolo_model.name} {yolo_model.version}",
model=yolo_model,
user=request.user,
user_name=request.user.username,
object_count=object_count,
detected_categories=detected_categories,
confidence_threshold=0.5,
confidence_scores=confidence_scores,
avg_confidence=avg_confidence,
processing_time=processing_time
)
return HertzResponse.success(
data={
'id': detection_record.id,
'file': {
'result_url': detection_record.result_file.url,
'original_url': detection_record.original_file.url
},
'stats': {
'count': object_count,
'categories': detected_categories,
'scores': confidence_scores,
'avg_score': round(avg_confidence, 4) if avg_confidence is not None else None,
'time': round(processing_time, 2)
},
'model': {
'name': yolo_model.name,
'version': yolo_model.version,
'threshold': 0.5
},
'user': {
'id': getattr(request.user, 'user_id', None),
'name': request.user.username
}
},
message='检测完成'
)
```