403 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
#!/usr/bin/env python
 | 
						||
# coding:utf-8
 | 
						||
import json
 | 
						||
import os
 | 
						||
import sys
 | 
						||
 | 
						||
import config
 | 
						||
import utils
 | 
						||
 | 
						||
class CDNConfig:
 | 
						||
    enable = False
 | 
						||
    dir = ''
 | 
						||
    filename = ''
 | 
						||
    version = ''
 | 
						||
 | 
						||
    def __init__(self, enable: bool, dir: str, filename: str, version: str):
 | 
						||
        self.enable = enable
 | 
						||
        self.dir = dir
 | 
						||
        self.filename = filename
 | 
						||
        self.version = version
 | 
						||
 | 
						||
class RemoteConfig:
 | 
						||
    enable = False
 | 
						||
    key = ''
 | 
						||
    group = ''
 | 
						||
    condition = ''
 | 
						||
 | 
						||
    def __init__(self, enable, key, group, condition):
 | 
						||
        self.enable = enable
 | 
						||
        self.key = key
 | 
						||
        self.group = group
 | 
						||
        self.condition = condition
 | 
						||
 | 
						||
class ConfigGenerator:
 | 
						||
    google_sheet_file_name = None
 | 
						||
    sheet_table_name = None
 | 
						||
    project = None
 | 
						||
    platform = None
 | 
						||
    env = None
 | 
						||
    cdn_config = None
 | 
						||
    remote_config = None
 | 
						||
    sheet_helper = None
 | 
						||
    firebase_helper = None
 | 
						||
 | 
						||
    field_dict = {}         # 字段名称字典
 | 
						||
    field_type_dict = {}    # 字段类型字典
 | 
						||
 | 
						||
    # region 配置生成器初始化
 | 
						||
    def __init__(self, google_sheet_file_name, sheet_table_name, project, platform, env):
 | 
						||
        self.sheet_helper = config.sheet_helper
 | 
						||
        self.google_sheet_file_name = google_sheet_file_name
 | 
						||
        self.sheet_table_name = sheet_table_name
 | 
						||
        self.project = project
 | 
						||
        self.platform = platform.lower()
 | 
						||
        self.env = env
 | 
						||
        self.init_notification_param()
 | 
						||
        self.init_config_generator_param()
 | 
						||
 | 
						||
    def init_notification_param(self):
 | 
						||
        config.notification.append_msg(f'生成配置文件[文件名: {self.google_sheet_file_name}, 表名: {self.sheet_table_name}]')
 | 
						||
        config.notification.append_msg(f'配置构建参数[项目: {self.project}, 平台: {self.platform}, 环境: {self.env}]')
 | 
						||
 | 
						||
    def init_config_generator_param(self):
 | 
						||
        sheet = self.sheet_helper.get_sheet_table(self.google_sheet_file_name, self.sheet_table_name)
 | 
						||
        if sheet is None:
 | 
						||
            config.wechat_alert_exception(f'获取表格失败[文件名: {self.google_sheet_file_name}, 表名: {self.sheet_table_name}]')
 | 
						||
        sheet_all_row_datas = sheet.get_all_values(major_dimension='ROWS')
 | 
						||
        for i, row_values in enumerate(sheet_all_row_datas):
 | 
						||
            if i == 0:
 | 
						||
                self.parse_cdn_config(row_values)
 | 
						||
            elif i == 1:
 | 
						||
                self.parse_remote_config(row_values)
 | 
						||
            elif i == 3:
 | 
						||
                self.parse_field(row_values, self.field_dict)
 | 
						||
            elif i == 4:
 | 
						||
                self.parse_field_type(row_values, self.field_type_dict)
 | 
						||
            else:
 | 
						||
                break
 | 
						||
 | 
						||
    def parse_cdn_config(self, row_values):
 | 
						||
        enable_index = -1
 | 
						||
        enable_value = None
 | 
						||
        dir_index = -1
 | 
						||
        dir_value = None
 | 
						||
        filename_index = -1
 | 
						||
        filename_value = None
 | 
						||
        version_index = -1
 | 
						||
        version_value = None
 | 
						||
 | 
						||
        for i, cell_value in enumerate(row_values):
 | 
						||
            if cell_value == 'enable':
 | 
						||
                enable_index = i + 1
 | 
						||
            elif cell_value == '目录':
 | 
						||
                dir_index = i + 1
 | 
						||
            elif cell_value == '文件名':
 | 
						||
                filename_index = i + 1
 | 
						||
            elif cell_value == '版本':
 | 
						||
                version_index = i + 1
 | 
						||
 | 
						||
        for i, cell_value in enumerate(row_values):
 | 
						||
            if i == enable_index:
 | 
						||
                if str(cell_value).lower() == 'true':
 | 
						||
                    enable_value = True
 | 
						||
            elif i == dir_index:
 | 
						||
                dir_value = cell_value
 | 
						||
            elif i == filename_index:
 | 
						||
                filename_value = cell_value
 | 
						||
            elif i == version_index:
 | 
						||
                version_value = cell_value
 | 
						||
        self.cdn_config = CDNConfig(enable_value, dir_value, filename_value, version_value)
 | 
						||
 | 
						||
    def parse_remote_config(self, row_values):
 | 
						||
        pass
 | 
						||
        enable_index = -1
 | 
						||
        enable_value = False
 | 
						||
        key_index = -1
 | 
						||
        key_value = None
 | 
						||
        group_index = -1
 | 
						||
        group_value = None
 | 
						||
        condition_index = -1
 | 
						||
        condition_value = None
 | 
						||
        for i, cell_value in enumerate(row_values):
 | 
						||
            if cell_value == 'enable':
 | 
						||
                enable_index = i + 1
 | 
						||
            if cell_value == 'key':
 | 
						||
                key_index = i + 1
 | 
						||
            elif cell_value == 'group':
 | 
						||
                group_index = i + 1
 | 
						||
            elif cell_value == 'condition':
 | 
						||
                condition_index = i + 1
 | 
						||
        for i, cell_value in enumerate(row_values):
 | 
						||
            if i == enable_index:
 | 
						||
                if str(cell_value).lower() == 'true':
 | 
						||
                    enable_value = True
 | 
						||
            elif i == key_index:
 | 
						||
                remote_key = cell_value
 | 
						||
                if '#platform#' in remote_key:
 | 
						||
                    remote_key = remote_key.replace('#platform#', self.platform)
 | 
						||
                key_value = remote_key
 | 
						||
            elif i == group_index:
 | 
						||
                group_value = cell_value
 | 
						||
            elif i == condition_index:
 | 
						||
                condition_value = cell_value
 | 
						||
        self.remote_config = RemoteConfig(enable_value, key_value, group_value, condition_value)
 | 
						||
 | 
						||
    def parse_field(self, row_values, field_dict):
 | 
						||
        for i, field_name in enumerate(row_values):
 | 
						||
            if i == 0:
 | 
						||
                continue
 | 
						||
            if field_name != '':
 | 
						||
                field_dict[i] = field_name
 | 
						||
        print(str(field_dict))
 | 
						||
 | 
						||
    def parse_field_type(self, row_values, field_type_dict):
 | 
						||
        for i, field_type in enumerate(row_values):
 | 
						||
            if i == 0:
 | 
						||
                continue
 | 
						||
            if field_type != '':
 | 
						||
                field_type_dict[i] = field_type
 | 
						||
        print(str(field_type_dict))
 | 
						||
    # endregion
 | 
						||
 | 
						||
    def gen_config_json(self):
 | 
						||
        sheet = self.sheet_helper.get_sheet_table(self.google_sheet_file_name, self.sheet_table_name)
 | 
						||
        sheet_all_row_datas = sheet.get_all_values(major_dimension='ROWS')
 | 
						||
        config_json = {'datas': []}
 | 
						||
        error_lines = []
 | 
						||
        data_row_index = -1
 | 
						||
        for i, row_values in enumerate(sheet_all_row_datas):
 | 
						||
            if data_row_index == -1:
 | 
						||
                if 'data' not in str(row_values[0]).lower():
 | 
						||
                    continue
 | 
						||
                else:
 | 
						||
                    data_row_index = i
 | 
						||
 | 
						||
            if self.is_row_env_valid(row_values) is False:
 | 
						||
                continue
 | 
						||
 | 
						||
            item_data, error = self.parse_row_data(i + 1, row_values, self.field_dict, self.field_type_dict)
 | 
						||
            if error != '':
 | 
						||
                error_lines.append(error)
 | 
						||
            elif len(item_data) == 0:
 | 
						||
                continue
 | 
						||
            else:
 | 
						||
                config_json['datas'].append(item_data)
 | 
						||
 | 
						||
        if len(error_lines) > 0:
 | 
						||
            config.notification.append_msg(str(error_lines))
 | 
						||
            return
 | 
						||
 | 
						||
        _json = json.dumps(config_json)
 | 
						||
        filename = self.get_config_filename()
 | 
						||
        local_file_path = self.get_local_config_file_path()
 | 
						||
        utils.write_json_file(local_file_path, _json)
 | 
						||
        config.notification.append_msg(f"{filename} 关卡配置生成成功!当前总关卡数:{len(config_json['datas'])}")
 | 
						||
        config.wechat_alert(f'{filename} json:{_json}')
 | 
						||
        print(f"{filename} json: {_json}")
 | 
						||
        return _json
 | 
						||
 | 
						||
    # region 行数据解析
 | 
						||
    def is_row_env_valid(self, row_values):
 | 
						||
        if self.env == config.env.debug.value:
 | 
						||
            return True
 | 
						||
        if row_values[1] == 'TRUE':
 | 
						||
            return True
 | 
						||
        return False
 | 
						||
 | 
						||
    def parse_row_data(self, row, row_values, field_dict, field_type_dict):
 | 
						||
        item_data = {}
 | 
						||
        error = ''
 | 
						||
        for i, value in enumerate(row_values):
 | 
						||
            if i not in field_dict or i not in field_type_dict:
 | 
						||
                continue
 | 
						||
 | 
						||
            field = field_dict[i]
 | 
						||
            field_type = field_type_dict[i]
 | 
						||
            isvalid, check_error = self.check_field_type_valid(field_type, value)
 | 
						||
            if isvalid:
 | 
						||
                item_data[field] = self.get_type_value(field_type, value)
 | 
						||
            else:
 | 
						||
                error += f'|{check_error}'
 | 
						||
        if error != '':
 | 
						||
            error = f"第{row}行->id:{item_data['id']}关卡配置错误: {error}"
 | 
						||
        return item_data, error
 | 
						||
 | 
						||
    def check_field_type_valid(self, field_type, value):
 | 
						||
        if value is None:
 | 
						||
            return False, f'值为空'
 | 
						||
 | 
						||
        if field_type == '': # 默认值
 | 
						||
            return True, ''
 | 
						||
 | 
						||
        try:
 | 
						||
            if field_type == 'int':
 | 
						||
                int(value)
 | 
						||
            elif field_type == 'float' or field_type == 'double':
 | 
						||
                float(value)
 | 
						||
            elif field_type == 'bool':
 | 
						||
                if value.lower() not in ['true', 'false']:
 | 
						||
                    return False, f'布尔值格式错误: {value}'
 | 
						||
            elif field_type == 'string':
 | 
						||
                # 字符串类型不需要特殊验证
 | 
						||
                pass
 | 
						||
            elif field_type.startswith('List<'):
 | 
						||
                inner_type = field_type[5:-1]  # 提取 List<type> 中的 type
 | 
						||
                if value == '':
 | 
						||
                    return True, ''  # 空列表是有效的
 | 
						||
                
 | 
						||
                items = value.split('#')
 | 
						||
                for item in items:
 | 
						||
                    if item == '':
 | 
						||
                        continue
 | 
						||
                    valid, error = self.check_field_type_valid(inner_type, item)
 | 
						||
                    if not valid:
 | 
						||
                        return False, f'列表元素 {item} {error}'
 | 
						||
            else:
 | 
						||
                return False, f'未知类型: {field_type}'
 | 
						||
                
 | 
						||
            return True, ''
 | 
						||
        except ValueError:
 | 
						||
            return False, f'类型转换错误: 无法将 {value} 转换为 {field_type}'
 | 
						||
        except Exception as e:
 | 
						||
            return False, f'验证错误: {str(e)}'
 | 
						||
 | 
						||
    def get_type_value(self, field_type, value):
 | 
						||
        if value is None:
 | 
						||
            return None
 | 
						||
 | 
						||
        if field_type == 'int':
 | 
						||
            if value == '':
 | 
						||
                return 0
 | 
						||
            return int(value)
 | 
						||
        elif field_type == 'float' or field_type == 'double':
 | 
						||
            if value == '':
 | 
						||
                return 0.0
 | 
						||
            return float(value)
 | 
						||
        elif field_type == 'bool':
 | 
						||
            if value == '':
 | 
						||
                return False
 | 
						||
            return value.lower() == 'true'
 | 
						||
        elif field_type == 'string':
 | 
						||
            return value
 | 
						||
        elif field_type.startswith('List<'):
 | 
						||
            if value == '':
 | 
						||
                return []
 | 
						||
            inner_type = field_type[5:-1]
 | 
						||
            items = value.split('#')
 | 
						||
            result = []
 | 
						||
            for item in items:
 | 
						||
                if item == '':
 | 
						||
                    continue
 | 
						||
                result.append(self.get_type_value(inner_type, item))
 | 
						||
            return result
 | 
						||
        else:
 | 
						||
            # 未知类型,返回原始值
 | 
						||
            return value
 | 
						||
    # endregion
 | 
						||
 | 
						||
    def upload_cdn(self):
 | 
						||
        if self.cdn_config is None:
 | 
						||
            config.notification.append_msg('cdn配置为空,不上传cdn')
 | 
						||
            return
 | 
						||
 | 
						||
        if self.cdn_config.enable is False:
 | 
						||
            config.notification.append_msg('cdn配置未开启, 不上传cdn')
 | 
						||
            return
 | 
						||
 | 
						||
        filename = self.get_config_filename()
 | 
						||
        local_file_path = self.get_local_config_file_path()
 | 
						||
        if not os.path.exists(local_file_path):
 | 
						||
            return
 | 
						||
        storage_level_db_path = f"{self.cdn_config.dir}/{filename}"
 | 
						||
        generation = config.get_firebase_instance(self.project).upload_file(local_file_path, storage_level_db_path)
 | 
						||
        config.notification.append_msg(f"{filename}配置上传到cdn路径:{storage_level_db_path}成功")
 | 
						||
        cdn_url = f'{config.get_project_cdn(self.project)}/{storage_level_db_path}?generation={generation}'
 | 
						||
        config.notification.append_msg(f"配置最新下载链接url:{cdn_url}")
 | 
						||
        return generation
 | 
						||
 | 
						||
    def update_remote_config(self, generation=None):
 | 
						||
        if self.remote_config is None:
 | 
						||
            config.notification.append_msg('remote配置为空,不更新RemoteConfig')
 | 
						||
            return
 | 
						||
 | 
						||
        if self.remote_config.enable is False:
 | 
						||
            config.notification.append_msg('remote配置未开启,不更新RemoteConfig')
 | 
						||
            return
 | 
						||
 | 
						||
        filename = self.get_config_filename()
 | 
						||
        storage_level_db_path = f"{self.cdn_config.dir}/{filename}"
 | 
						||
        cdn = config.get_project_cdn(self.project)
 | 
						||
        if generation is None:
 | 
						||
            generation = self.firebase_helper.get_file_generation(storage_level_db_path)
 | 
						||
        level_db_cdn_url = f'{cdn}/{storage_level_db_path}' if generation is None else f'{cdn}/{storage_level_db_path}?generation={generation}'
 | 
						||
        key = self.remote_config.key
 | 
						||
        group = None if self.remote_config.group == '' else self.remote_config.group
 | 
						||
        condition = None if self.remote_config.condition == '' else self.remote_config.condition
 | 
						||
        if self.project is not None:
 | 
						||
            config.get_firebase_instance(self.project).update_remote_config_json_value(group, condition, key, self.env, level_db_cdn_url, True)
 | 
						||
            config.notification.append_msg(f"[group:{group}, condition:{condition}, key:{key} env:{self.env}] 云控更新成功")
 | 
						||
 | 
						||
    def get_local_config_file_path(self):
 | 
						||
        local_file_path = f'temp_config/{self.get_config_filename()}'
 | 
						||
        return local_file_path
 | 
						||
 | 
						||
    def get_config_filename(self):
 | 
						||
        cdn_filename = self.cdn_config.filename
 | 
						||
        if '#platform#' in cdn_filename:
 | 
						||
            if self.platform == config.platform.All.value or self.platform == config.platform.No.value:
 | 
						||
                cdn_filename = cdn_filename.replace('#platform#', '')
 | 
						||
            else:
 | 
						||
                cdn_filename = cdn_filename.replace('#platform#', self.platform)
 | 
						||
        cdn_filename += f'-{self.env}.json'
 | 
						||
        return cdn_filename
 | 
						||
 | 
						||
project_id = None
 | 
						||
platform = None
 | 
						||
env = None
 | 
						||
google_sheet_file_name = None
 | 
						||
sheet_table_name = None
 | 
						||
param_enable_upload_cdn = None
 | 
						||
param_enable_upload_remote_config = None
 | 
						||
 | 
						||
if len(sys.argv) > 1:
 | 
						||
    project_id = sys.argv[1]
 | 
						||
if len(sys.argv) > 2:
 | 
						||
    platform = sys.argv[2]
 | 
						||
if len(sys.argv) > 3:
 | 
						||
    env = sys.argv[3]
 | 
						||
if len(sys.argv) > 4:
 | 
						||
    google_sheet_file_name = sys.argv[4]
 | 
						||
if len(sys.argv) > 5:
 | 
						||
    sheet_table_name = sys.argv[5]
 | 
						||
if len(sys.argv) > 6:
 | 
						||
    param_enable_upload_cdn = sys.argv[6]
 | 
						||
if len(sys.argv) > 7:
 | 
						||
    param_enable_upload_remote_config = sys.argv[7]
 | 
						||
 | 
						||
if __name__ == "__main__":
 | 
						||
    if project_id is None:
 | 
						||
        config.notification.append_msg(f'参数错误[project_id is None]')
 | 
						||
        exit(1)
 | 
						||
 | 
						||
    if platform is None:
 | 
						||
        config.notification.append_msg(f'参数错误[platform is None]')
 | 
						||
        exit(1)
 | 
						||
 | 
						||
    if env is None:
 | 
						||
        config.notification.append_msg(f'参数错误[env is None]')
 | 
						||
        exit(1)
 | 
						||
 | 
						||
    if google_sheet_file_name is None or sheet_table_name is None:
 | 
						||
        config.notification.append_msg(f'参数错误[google_sheet_file_name or sheet_table_name is None]')
 | 
						||
        exit(1)
 | 
						||
 | 
						||
    config_generator = ConfigGenerator(google_sheet_file_name, sheet_table_name, project_id, platform, env)
 | 
						||
    config_json = config_generator.gen_config_json()
 | 
						||
    generation = None
 | 
						||
    if str(param_enable_upload_cdn).lower() is 'true':
 | 
						||
        generation = config_generator.upload_cdn()
 | 
						||
    if str(param_enable_upload_remote_config).lower() is 'true':
 | 
						||
        config_generator.update_remote_config(generation)
 | 
						||
 |