Compare commits

...

2 Commits

Author SHA1 Message Date
caoqianming ce35998315 release: 3.1.2026071614
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 14:48:09 +08:00
caoqianming 5881c32c38 perf:mlog/handover/mlogb列表消除N+1,select_related精简改prefetch,修Handover.handoverb属性绕过prefetch的bug
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 14:41:46 +08:00
5 changed files with 40 additions and 30 deletions

View File

@ -721,7 +721,8 @@ class Handover(CommonADModel):
@property @property
def handoverb(self): def handoverb(self):
return Handoverb.objects.filter(handover=self) # 用反向关联管理器, 以命中 list 接口的 Prefetch("b_handover") 缓存, 避免 N+1
return self.b_handover.all()
class Handoverb(BaseModel): class Handoverb(BaseModel):
"""TN: 子级交接记录 """TN: 子级交接记录

View File

@ -285,6 +285,7 @@ class MlogViewSet(CustomModelViewSet):
"handle_user", "handle_user",
"equipment", "equipment",
"mgroup", "mgroup",
"mgroup__belong_dept",
"material_in", "material_in",
"material_out", "material_out",
"material_in__process", "material_in__process",
@ -298,7 +299,22 @@ class MlogViewSet(CustomModelViewSet):
# 'equipment_2', 'material_in', 'material_out', 'route__routepack', 'submit_user', # 'equipment_2', 'material_in', 'material_out', 'route__routepack', 'submit_user',
# 'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user', 'team', # 'supplier', 'ticket', 'mgroup__process', 'test_user', 'handle_leader', 'test_user', 'team',
# 'material_in__process', 'material_out__process', "shift", "route"] # 'material_in__process', 'material_out__process', "shift", "route"]
prefetch_related_fields = ["handle_users", "material_outs", "equipments"] prefetch_related_fields = [
"handle_users",
"material_outs",
"equipments",
# 以下为可选的一对一/外键, 用 prefetch(每个关系仅 1 条 IN 查询)避免 N+1
# 又不加宽主查询的 JOIN
"team",
"supplier",
"mtask",
"shift",
"test_user",
"handle_user_2",
"handle_leader",
"equipment_2",
"ticket__state",
]
filterset_class = MlogFilter filterset_class = MlogFilter
search_fields = [ search_fields = [
"material_in__name", "material_in__name",
@ -342,27 +358,7 @@ class MlogViewSet(CustomModelViewSet):
] ]
) )
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
from django.db import connection return super().list(request, *args, **kwargs)
from django.conf import settings
# 清空之前的查询记录
connection.queries_log.clear()
# 调用父类的 list 方法
response = super().list(request, *args, **kwargs)
# 获取执行的 SQL 查询
queries = connection.queries
sql_queries = []
for query in queries:
sql_queries.append({"sql": query["sql"], "time": query["time"], "params": query.get("params", [])})
# 将 SQL 信息添加到响应中(仅在调试模式)
if settings.DEBUG:
response.data["_debug"] = {"sql_queries": sql_queries, "total_queries": len(queries), "total_time": sum(float(q["time"]) for q in queries)}
return response
def add_info_for_list(self, data): def add_info_for_list(self, data):
czx_dict = {} czx_dict = {}
@ -590,10 +586,10 @@ class HandoverViewSet(CustomModelViewSet):
queryset = Handover.objects.all() queryset = Handover.objects.all()
list_serializer_class = HandoverListSerializer list_serializer_class = HandoverListSerializer
serializer_class = HandoverSerializer serializer_class = HandoverSerializer
select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material"] select_related_fields = ["send_user", "send_mgroup", "send_dept", "recive_user", "recive_mgroup", "recive_dept", "wm", "material_changed", "material", "material__process"]
filterset_class = HandoverFilter filterset_class = HandoverFilter
search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"] search_fields = ["material__name", "material__number", "material__specification", "batch", "material__model", "b_handover__batch", "new_batch", "wm__batch"]
prefetch_related_fields = [Prefetch("b_handover", queryset=Handoverb.objects.select_related("wm__defect"))] prefetch_related_fields = [Prefetch("b_handover", queryset=Handoverb.objects.select_related("wm__defect")), "ticket__state"]
def perform_destroy(self, instance: Handover): def perform_destroy(self, instance: Handover):
user = self.request.user user = self.request.user
@ -782,7 +778,7 @@ class MlogbViewSet(CustomListModelMixin, CustomGenericViewSet):
perms_map = {"get": "*"} perms_map = {"get": "*"}
queryset = Mlogb.objects.all() queryset = Mlogb.objects.all()
serializer_class = MlogbDetailSerializer serializer_class = MlogbDetailSerializer
select_related_fields = ["material_out", "material_in", "test_user", "wm_in__defect"] select_related_fields = ["material_out", "material_out__process", "material_in", "material_in__process", "test_user", "wm_in__defect"]
prefetch_related_fields = [ prefetch_related_fields = [
Prefetch("mlogbdefect_set", queryset=MlogbDefect.objects.select_related("defect")), Prefetch("mlogbdefect_set", queryset=MlogbDefect.objects.select_related("defect")),
] ]

View File

@ -1,3 +1,13 @@
## 3.1.2026071614
- feat: 新增功能
- 合格B类字段前端文案改为"记为合格"更准确表达 [caoqianming]
- fix: 问题修复
- 发货编号获取接口number_out_last兼容无横杠格式避免明细重复编号 [caoqianming]
- other: 其他变更
- perf:mlog/handover/mlogb列表消除N+1,select_related精简改prefetch,修Handover.handoverb属性绕过prefetch的bug [caoqianming]
- 车间库存列表补齐关联字段select_related,消除剩余N+1 [caoqianming]
- perf:销售发货批次隐藏可发数量为0的批次,并用子查询消除在途量N+1 [caoqianming]
## 3.1.2026071608 ## 3.1.2026071608
- feat: 新增功能 - feat: 新增功能

View File

@ -35,7 +35,7 @@ sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
SYS_NAME = '星途工厂综合管理系统' SYS_NAME = '星途工厂综合管理系统'
SYS_VERSION = '3.1.2026071608' SYS_VERSION = '3.1.2026071614'
X_FRAME_OPTIONS = 'SAMEORIGIN' X_FRAME_OPTIONS = 'SAMEORIGIN'
# Application definition # Application definition

View File

@ -33,9 +33,12 @@ process_commits() {
local type="$1" local type="$1"
local header="$2" local header="$2"
local pattern="$3" local pattern="$3"
local invert="$4" # 传 "invert" 则取"不匹配 pattern 的提交"(用于 other 兜底)
# 查找匹配类型的提交 # 查找匹配类型的提交
git log $RANGE --no-merges --pretty=format:"%s | %an | %ad" --date=short | grep -E "$pattern" | while read -r line; do local grep_flag="-E"
[ "$invert" = "invert" ] && grep_flag="-vE"
git log $RANGE --no-merges --pretty=format:"%s | %an | %ad" --date=short | grep $grep_flag "$pattern" | while read -r line; do
COMMIT_MSG=$(echo "$line" | cut -d'|' -f1 | sed 's/^ *//;s/ *$//') COMMIT_MSG=$(echo "$line" | cut -d'|' -f1 | sed 's/^ *//;s/ *$//')
AUTHOR=$(echo "$line" | cut -d'|' -f2 | sed 's/^ *//;s/ *$//') AUTHOR=$(echo "$line" | cut -d'|' -f2 | sed 's/^ *//;s/ *$//')
DATE=$(echo "$line" | cut -d'|' -f3 | sed 's/^ *//;s/ *$//') DATE=$(echo "$line" | cut -d'|' -f3 | sed 's/^ *//;s/ *$//')
@ -55,7 +58,7 @@ process_commits() {
# 处理各类型提交(按优先级排序) # 处理各类型提交(按优先级排序)
printed_header=0; process_commits "feat" "feat: 新增功能" "^[fF]eat" printed_header=0; process_commits "feat" "feat: 新增功能" "^[fF]eat"
printed_header=0; process_commits "fix" "fix: 问题修复" "^[fF]ix" printed_header=0; process_commits "fix" "fix: 问题修复" "^[fF]ix"
printed_header=0; process_commits "" "other: 其他变更" "^((?![fF]eat|[fF]ix).)*$" printed_header=0; process_commits "" "other: 其他变更" "^([fF]eat|[fF]ix)" "invert"
# 合并到原文件 # 合并到原文件
if [ -f changelog.md ]; then if [ -f changelog.md ]; then