import re import logging logger = logging.getLogger(__name__) USER_ID_PATTERN = re.compile(r'^[a-zA-Z0-9]+$') def validate_user_id(user_id): if not user_id: return False, "人员ID不能为空" if not USER_ID_PATTERN.match(user_id): return False, "人员ID仅支持大小写英文字母和数字" if len(user_id) > 50: return False, "人员ID长度不能超过50" return True, "" def validate_user_name(user_name): if not user_name: return False, "人员姓名不能为空" if len(user_name) > 100: return False, "人员姓名长度不能超过100" return True, "" def validate_height(height): if height is None or height == "": return True, None try: h = float(height) if 0.1 <= h <= 10.0: return True, round(h, 1) return False, f"高度 {height} 超出有效范围 0.1~10.0" except (ValueError, TypeError): return False, f"高度 {height} 不是有效数值" def validate_noise_level(level): if level is None or level == "": return True, 0 try: n = int(level) if 0 <= n <= 10: return True, n return False, f"噪声强度 {level} 超出有效范围 0~10" except (ValueError, TypeError): return False, f"噪声强度 {level} 不是有效整数" def validate_rotate(min_val, max_val): def _clamp(v): try: v = int(v) return max(-180, min(180, v)) except (ValueError, TypeError): return 0 r_min = _clamp(min_val) if min_val not in (None, "") else 0 r_max = _clamp(max_val) if max_val not in (None, "") else 0 return True, (r_min, r_max) def validate_match_mode(mode): if mode in ('upload', 'id', 'name', 'auto'): return True, "" return False, f"匹配模式 '{mode}' 无效,支持: upload/id/name/auto" def validate_docx_name(name): if not name: return False, "文件名不能为空" if not name.lower().endswith('.docx'): return False, "仅支持.docx格式" return True, "" def validate_docx_size(data_bytes, max_bytes): if not isinstance(data_bytes, (bytes, bytearray)): return False, "文档数据无效" if len(data_bytes) > max_bytes: return False, f"文档大小 {len(data_bytes)} 字节超过限制 {max_bytes} 字节" return True, "" def validate_image_size(data_bytes, max_bytes): if not isinstance(data_bytes, (bytes, bytearray)): return False, "图片数据无效" if len(data_bytes) > max_bytes: return False, f"图片大小 {len(data_bytes)} 字节超过限制 {max_bytes} 字节" return True, "" def validate_capture_group(group): if group is None or group == "": return False, "捕获组序号不能为空" try: g = int(group) except (ValueError, TypeError): return False, f"捕获组序号 '{group}' 不是整数" if g < 1: return False, "捕获组序号禁止为 0 或负数,必须 >=1" return True, g def validate_rule(rule): errors = [] if not rule.get('name'): errors.append("规则名称不能为空") pattern = rule.get('pattern') if not pattern: errors.append("规则正则表达式不能为空") else: try: re.compile(pattern) except re.error as e: errors.append(f"正则表达式非法: {e}") valid, msg_or_val = validate_capture_group(rule.get('capture_group')) if not valid: errors.append(msg_or_val) cell_mode = rule.get('cell_mode', 'single') if cell_mode not in ('single', 'horizontal', 'vertical'): errors.append(f"表格方式 '{cell_mode}' 无效") cell_match = rule.get('cell_match', 'partial') if cell_match not in ('partial', 'whole'): errors.append(f"匹配方式 '{cell_match}' 无效") return errors def validate_sign_request(params): errors = [] valid, msg = validate_docx_name(params.get('docx_name', '')) if not valid: errors.append(msg) valid, msg = validate_match_mode(params.get('match_mode', '')) if not valid: errors.append(msg) stamps = params.get('stamps', []) if params.get('match_mode') != 'auto' and not stamps: errors.append("非auto模式必须提供盖章项列表") if len(stamps) > 100: errors.append("盖章项不能超过100个") height = params.get('height') if height is not None and height != "": valid, _ = validate_height(height) if not valid: errors.append(f"全局高度无效: {validate_height(height)[1]}") noise = params.get('noise_level') if noise is not None and noise != "": valid, _ = validate_noise_level(noise) if not valid: errors.append(f"全局噪声无效: {validate_noise_level(noise)[1]}") return errors