diff --git a/backend/views/titles.py b/backend/views/titles.py index 4a63e10..399efcc 100644 --- a/backend/views/titles.py +++ b/backend/views/titles.py @@ -175,6 +175,58 @@ class KhpzhqView(APIView): +_CHENGHAO_LEIXING_OK = frozenset({'dashou', 'boss', 'shangjia', 'guanshi', 'zuzhang'}) +_CHENGHAO_LEIXING_ALIAS = { + '打手': 'dashou', + '老板': 'boss', + '商家': 'shangjia', + '管事': 'guanshi', + '组长': 'zuzhang', +} + + +def _normalize_chenghao_leixing(raw, default='dashou'): + """兼容 null / 中文显示名 / 首尾空格,统一为合法 leixing 代码。""" + if raw is None or raw == '': + return default + s = str(raw).strip() + if not s: + return default + if s in _CHENGHAO_LEIXING_OK: + return s + mapped = _CHENGHAO_LEIXING_ALIAS.get(s) or _CHENGHAO_LEIXING_ALIAS.get(s.lower()) + if mapped: + return mapped + lower = s.lower() + if lower in _CHENGHAO_LEIXING_OK: + return lower + return None + + +def _parse_optional_bankuai_id(raw): + """空值视为未绑定板块;非法/不存在返回 False。""" + if raw is None or raw == '': + return None + try: + bid = int(raw) + except (TypeError, ValueError): + return False + if bid <= 0: + return None + if not Bankuai.query.filter(bankuai_id=bid).exists(): + return False + return bid + + +def _parse_chenghao_id(raw): + if raw is None or str(raw).strip() == '': + return None + try: + return int(raw) + except (TypeError, ValueError): + return None + + class ChzsgcView(APIView): """ 称号(标签)增删改查统一接口 @@ -197,18 +249,21 @@ class ChzsgcView(APIView): # ========== 创建称号 ========== if action == 'create': - mingcheng = request.data.get('mingcheng', '').strip() - leixing = request.data.get('leixing', 'dashou') - bankuai_id = request.data.get('bankuai_id') - texiao_miaoshu = request.data.get('texiao_miaoshu', '') - kaohe_guize = request.data.get('kaohe_guize', '') - kaioi_jinpai = request.data.get('kaioi_jinpai', False) + mingcheng = (request.data.get('mingcheng') or '').strip() + leixing = _normalize_chenghao_leixing(request.data.get('leixing'), default='dashou') + bankuai_id = _parse_optional_bankuai_id(request.data.get('bankuai_id')) + texiao_miaoshu = request.data.get('texiao_miaoshu', '') or '' + kaohe_guize = request.data.get('kaohe_guize', '') or '' + kaioi_jinpai = bool(request.data.get('kaioi_jinpai', False)) feiyong_list = request.data.get('feiyong_list', []) if not mingcheng: return Response({'code': 1, 'msg': '称号名称不能为空'}, status=400) - if leixing not in ['dashou','boss','shangjia','guanshi','zuzhang']: + if leixing is None: + logger.warning('ChzsgcView create 无效 leixing=%r', request.data.get('leixing')) return Response({'code': 1, 'msg': '无效的称号类型'}, status=400) + if bankuai_id is False: + return Response({'code': 1, 'msg': '板块不存在'}, status=400) if Chenghao.query.filter(mingcheng=mingcheng).exists(): return Response({'code': 1, 'msg': '称号名称已存在'}, status=400) @@ -238,7 +293,7 @@ class ChzsgcView(APIView): # ========== 修改称号 ========== elif action == 'update': - ch_id = request.data.get('chenghao_id') + ch_id = _parse_chenghao_id(request.data.get('chenghao_id')) if not ch_id: return Response({'code': 1, 'msg': '缺少称号ID'}, status=400) try: @@ -246,17 +301,25 @@ class ChzsgcView(APIView): except Chenghao.DoesNotExist: return Response({'code': 1, 'msg': '称号不存在'}, status=404) - mingcheng = request.data.get('mingcheng', ch.mingcheng).strip() - leixing = request.data.get('leixing', ch.leixing) - bankuai_id = request.data.get('bankuai_id', ch.bankuai_id) + mingcheng = (request.data.get('mingcheng') if request.data.get('mingcheng') is not None else ch.mingcheng) or '' + mingcheng = str(mingcheng).strip() + raw_leixing = request.data.get('leixing', ch.leixing) + leixing = _normalize_chenghao_leixing(raw_leixing, default=ch.leixing or 'dashou') + if 'bankuai_id' in request.data: + bankuai_id = _parse_optional_bankuai_id(request.data.get('bankuai_id')) + else: + bankuai_id = ch.bankuai_id texiao_miaoshu = request.data.get('texiao_miaoshu', ch.texiao_miaoshu) kaohe_guize = request.data.get('kaohe_guize', ch.kaohe_guize) kaioi_jinpai = request.data.get('kaioi_jinpai', ch.kaioi_jinpai) if not mingcheng: return Response({'code': 1, 'msg': '称号名称不能为空'}, status=400) - if leixing not in ['dashou','boss','shangjia','guanshi','zuzhang']: + if leixing is None: + logger.warning('ChzsgcView update 无效 leixing=%r ch_id=%s', raw_leixing, ch_id) return Response({'code': 1, 'msg': '无效的称号类型'}, status=400) + if bankuai_id is False: + return Response({'code': 1, 'msg': '板块不存在'}, status=400) if Chenghao.query.filter(mingcheng=mingcheng).exclude(id=ch_id).exists(): return Response({'code': 1, 'msg': '称号名称已存在'}, status=400) @@ -265,7 +328,7 @@ class ChzsgcView(APIView): ch.bankuai_id = bankuai_id ch.texiao_miaoshu = texiao_miaoshu ch.kaohe_guize = kaohe_guize - ch.kaioi_jinpai = kaioi_jinpai + ch.kaioi_jinpai = bool(kaioi_jinpai) ch.save() # 如果前端传递了 feiyong_list,则全量替换费用 @@ -284,7 +347,7 @@ class ChzsgcView(APIView): # ========== 删除称号 ========== elif action == 'delete': - ch_id = request.data.get('chenghao_id') + ch_id = _parse_chenghao_id(request.data.get('chenghao_id')) if not ch_id: return Response({'code': 1, 'msg': '缺少称号ID'}, status=400) try: @@ -295,11 +358,11 @@ class ChzsgcView(APIView): return Response({'code': 0, 'msg': '删除成功'}) # ========== 添加/修改某次费用 ========== - elif action == 'add_feiyong': - ch_id = request.data.get('chenghao_id') + elif action in ('add_feiyong', 'update_feiyong'): + ch_id = _parse_chenghao_id(request.data.get('chenghao_id')) cishu = request.data.get('cishu') feiyong = request.data.get('feiyong', 0) - if not ch_id or not cishu: + if not ch_id or cishu is None or cishu == '': return Response({'code': 1, 'msg': '缺少称号ID或次数'}, status=400) try: ch = Chenghao.query.get(id=ch_id) @@ -308,16 +371,16 @@ class ChzsgcView(APIView): if int(cishu) < 1: return Response({'code': 1, 'msg': '次数必须>=1'}, status=400) KaoheCishuFeiyong.query.update_or_create( - chenghao=ch, cishu=cishu, + chenghao=ch, cishu=int(cishu), defaults={'feiyong': feiyong} ) return Response({'code': 0, 'msg': '添加/修改费用成功'}) # ========== 删除某次费用 ========== elif action == 'remove_feiyong': - ch_id = request.data.get('chenghao_id') + ch_id = _parse_chenghao_id(request.data.get('chenghao_id')) cishu = request.data.get('cishu') - if not ch_id or not cishu: + if not ch_id or cishu is None or cishu == '': return Response({'code': 1, 'msg': '缺少称号ID或次数'}, status=400) if int(cishu) == 1: return Response({'code': 1, 'msg': '不能删除第1次考核费用'}, status=400) @@ -325,13 +388,9 @@ class ChzsgcView(APIView): ch = Chenghao.query.get(id=ch_id) except Chenghao.DoesNotExist: return Response({'code': 1, 'msg': '称号不存在'}, status=404) - KaoheCishuFeiyong.query.filter(chenghao=ch, cishu=cishu).delete() + KaoheCishuFeiyong.query.filter(chenghao=ch, cishu=int(cishu)).delete() return Response({'code': 0, 'msg': '删除成功'}) - # ========== 修改某次费用(等价于add) ========== - elif action == 'update_feiyong': - return self.add_feiyong(request) # 复用逻辑 - else: return Response({'code': 1, 'msg': '未知操作'}, status=400)