""" License业务逻辑服务层 负责处理所有与License相关的业务逻辑,将API层与模型层解耦 """ from typing import Optional, Tuple, List from flask import current_app from sqlalchemy import func, or_ from app import db from app.models import License, Product from app.utils.logger import log_operation import secrets import string class LicenseService: """卡密业务逻辑服务""" @staticmethod def generate_license_key(length: int = 32, prefix: str = '') -> str: """ 生成卡密密钥 :param length: 密钥长度 :param prefix: 前缀 :return: 生成的密钥 """ # 生成指定长度的随机字符串 chars = string.ascii_uppercase + string.digits random_key = ''.join(secrets.choice(chars) for _ in range(length)) return f"{prefix}{random_key}" if prefix else random_key @staticmethod def create_license( product_id: str, valid_days: int, license_type: int = 0, max_bind_times: int = 1, remark: str = '' ) -> Tuple[bool, str, Optional[License]]: """ 创建卡密 :param product_id: 产品ID :param valid_days: 有效天数 :param license_type: 卡密类型 :param max_bind_times: 最大绑定次数 :param remark: 备注 :return: (是否成功, 消息, 卡密对象) """ try: # 检查产品是否存在 product = Product.query.filter_by(product_id=product_id, status=1).first() if not product: return False, "产品不存在或已下架", None # 生成唯一的卡密 max_attempts = 10 for _ in range(max_attempts): # 生成卡密 prefix = current_app.config.get('TRIAL_PREFIX', 'TRIAL_') if license_type == 1 else '' length = current_app.config.get('LICENSE_KEY_LENGTH', 32) license_key = LicenseService.generate_license_key(length, prefix) # 检查是否已存在 existing = License.query.filter_by(license_key=license_key).first() if not existing: break else: return False, "生成卡密失败,请重试", None # 创建卡密 license = License( license_key=license_key, product_id=product_id, valid_days=valid_days, type=license_type, max_bind_times=max_bind_times, remark=remark, status=0 # 未激活 ) db.session.add(license) db.session.commit() return True, "卡密创建成功", license except Exception as e: db.session.rollback() current_app.logger.error(f"创建卡密失败: {str(e)}") return False, f"创建卡密失败: {str(e)}", None @staticmethod def batch_create_licenses( product_id: str, valid_days: int, count: int, license_type: int = 0, max_bind_times: int = 1, remark: str = '' ) -> Tuple[bool, str, List[License]]: """ 批量创建卡密 :param product_id: 产品ID :param valid_days: 有效天数 :param count: 数量 :param license_type: 卡密类型 :param max_bind_times: 最大绑定次数 :param remark: 备注 :return: (是否成功, 消息, 卡密列表) """ try: licenses = [] for _ in range(count): success, msg, license = LicenseService.create_license( product_id, valid_days, license_type, max_bind_times, remark ) if not success: return False, f"创建失败: {msg}", [] licenses.append(license) return True, f"成功创建{count}个卡密", licenses except Exception as e: db.session.rollback() current_app.logger.error(f"批量创建卡密失败: {str(e)}") return False, f"批量创建卡密失败: {str(e)}", [] @staticmethod def get_licenses( page: int = 1, per_page: int = 20, keyword: str = '', license_type: Optional[int] = None, status: Optional[int] = None, product_id: str = '' ) -> Tuple[List[License], int]: """ 获取卡密列表(支持分页和筛选) :param page: 页码 :param per_page: 每页数量 :param keyword: 关键词 :param license_type: 卡密类型筛选 :param status: 状态筛选 :param product_id: 产品ID筛选 :return: (卡密列表, 总数) """ query = License.query.join(Product) # 关键词搜索 if keyword: # 转义特殊字符,防止LIKE查询中的通配符攻击 escaped_keyword = keyword.replace('%', '\\%').replace('_', '\\_') pattern = f'%{escaped_keyword.lower()}%' query = query.filter( or_( func.lower(License.license_key).like(pattern, escape='\\'), func.lower(Product.product_name).like(pattern, escape='\\') ) ) # 筛选条件 if license_type is not None: query = query.filter(License.type == license_type) if status is not None: query = query.filter(License.status == status) if product_id: query = query.filter(License.product_id == product_id) # 排序 query = query.order_by(License.create_time.desc()) # 分页 pagination = query.paginate(page=page, per_page=per_page, error_out=False) return pagination.items, pagination.total @staticmethod def verify_license(license_key: str, machine_code: str, software_version: str) -> Tuple[bool, str, Optional[License]]: """ 验证卡密 :param license_key: 卡密 :param machine_code: 机器码 :param software_version: 软件版本 :return: (是否成功, 消息, 卡密对象) """ try: license = License.query.filter_by(license_key=license_key).first() if not license: return False, "卡密不存在", None success, msg = license.verify(machine_code, software_version) return success, msg, license if success else None except Exception as e: current_app.logger.error(f"验证卡密失败: {str(e)}") return False, f"验证卡密失败: {str(e)}", None @staticmethod def unbind_license(license_key: str) -> Tuple[bool, str]: """ 解绑卡密 :param license_key: 卡密 :return: (是否成功, 消息) """ try: license = License.query.filter_by(license_key=license_key).first() if not license: return False, "卡密不存在" success, msg = license.unbind() return success, msg except Exception as e: db.session.rollback() current_app.logger.error(f"解绑卡密失败: {str(e)}") return False, f"解绑卡密失败: {str(e)}" @staticmethod def disable_license(license_key: str) -> Tuple[bool, str]: """ 禁用卡密 :param license_key: 卡密 :return: (是否成功, 消息) """ try: license = License.query.filter_by(license_key=license_key).first() if not license: return False, "卡密不存在" success, msg = license.disable() return success, msg except Exception as e: db.session.rollback() current_app.logger.error(f"禁用卡密失败: {str(e)}") return False, f"禁用卡密失败: {str(e)}" @staticmethod def delete_license(license_key: str) -> Tuple[bool, str]: """ 删除卡密(软删除) :param license_key: 卡密 :return: (是否成功, 消息) """ try: license = License.query.filter_by(license_key=license_key).first() if not license: return False, "卡密不存在" license.delete() # 假设有软删除方法 db.session.commit() return True, "删除成功" except Exception as e: db.session.rollback() current_app.logger.error(f"删除卡密失败: {str(e)}") return False, f"删除卡密失败: {str(e)}"