E3.Tree开源,发布1.0版本.

E3.Tree是E3平台下的组件,用于简化树型UI如菜单、树状视图等的开发过程。本文档介绍了其系统要求、示例部署步骤及如何通过不同类型的Builder创建普通树、带复选框或单选按钮的树。
E3.Tree 参考手册 (v0.1)
简介
E3.Tree是E3平台下一个用于构造 树型 UI(menu,tree,outlookbar等)的的组件,开发这个组件的主要目的有两点:
1.        重用树型UI的 构造过程,简化树型UI的开发
2.         建立起 一致的树型UI 开发模式
 
系统要求
JDK1.4X
TOMCAT5.X (其他未测试)
样例部署
把e3.war 放到 Tomcat's webapps 目录下,启动服务器,输入地址 http://localhost:8080/e3 进入示例主页 . 点级 E3.Tree 连接,即可看到示例程序.
示例组图:
使用
Lib文件清单

文件名
版本
说明
E3-Tree.jar
1.0
E3平台的树
E3-TemplateEngine.ja
1.0
E3平台的模板引擎Adapter
commons-logging.jar
1.04
Apache的commons log,
log4j-1.2.14.jar
1.2.14
Apache的log4j
velocity-1.4.jar
1.4
Apache的模板引擎

 
  
添加JAR到classpath中
把Lib文件清单中的jar全部添加到classpath中
web.xml配置
添加导入js 等UI资源的监听器

   <listener></listener>    <listener-class></listener-class> net.jcreate.e3.tree.loader.LoadResourcesListener
   

 
控制器Servlet

protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException {          
       //业务数据
              List orgs = new ArrayList();
              Org jcjtOrg = new Org("001",null,"进创集团", 1);
              Org jcrjOrg = new Org("001001","001","进创软件", 1);
              Org xrjOrg = new Org("0010010011","001001","X软件公司", 1);
              Org yrjOrg = new Org("0010010012","001001","Y软件公司", 2);
              Org zrjOrg = new Org("0010010013","001001","Z软件公司", 3);
              orgs.add(jcjtOrg);
              orgs.add(jcrjOrg);
              orgs.add(xrjOrg);
              orgs.add(yrjOrg);
              orgs.add(zrjOrg);
 
              //业务数据解码器,从业务数据中分解出id和parentid
              UserDataUncoder orgUncoder = new UserDataUncoder(){
                     public Object getID(Object pUserData) throws UncodeException {
                            Org org = (Org)pUserData;
                            return org.getId();
                     }
                     public Object getParentID(Object pUserData) throws UncodeException {
                            Org org = (Org)pUserData;
                            return org.getParentId();
                     }
              };
             
              //Tree模型构造器,用于生成树模型
              AbstractWebTreeModelCreator treeModelCreator =
               new AbstractWebTreeModelCreator(){
                     //该方法负责将业务数据映射到树型节点
                     protected Node createNode(Object pUserData, UserDataUncoder pUncoder) {
                            Org org = (Org)pUserData;
                            WebTreeNode result = new WebTreeNode(org.getName(), "org" + org.getId());
                            //action是点击按纽执行的方法.可以是url,或者javascript函数
                            result.setAction("javascript:alert(' " + org.getName() + "')");
                            return result;
                     }
              };
              treeModelCreator.init(pRequest);
             
              TreeModel treeModel = treeModelCreator.create(orgs,orgUncoder);
              TreeDirector director = new DefaultTreeDirector();//构造树导向器
              WebTreeBuilder treeBuilder = new XTreeBuilder();//构造树Builder
              treeBuilder.init(pRequest);            
              director.build(treeModel, treeBuilder);//执行构造        
              String treeScript = treeBuilder.getTreeScript();//获取构造树的脚本
              pRequest.setAttribute("treeScript", treeScript);//保存到request,以便页面使用
pRequest.getRequestDispatcher("/e3/samples/tree/XTree.jsp").forward(pRequest,pResponse);
}

 
这里我们构造是普通树,如果要构造带checkbox/radiobox的树,只需要将
WebTreeBuilder treeBuilder = new XTreeBuilder()
这行代码换成
WebTreeBuilder treeBuilder = new CheckXTreeBuilder ()
WebTreeBuilder treeBuilder = new RadioXTreeBuilder ()
即可
 
JSP页面

%@ page contentType="text/html; charset=utf-8"%>
< META http-equiv=Content-Type content="text/html; charset=utf-8">
 
 

from typing import List, Tuple, Optional, Callable import time import random import hashlib import psutil import uuid import os import platform import tkinter as tk from tkinter import filedialog, messagebox, ttk, simpledialog import threading from dataclasses import dataclass import qiskit from qiskit_aer import AerSimulator from qiskit_aer.noise import NoiseModel, depolarizing_error, thermal_relaxation_error from qiskit.primitives import Sampler try: from pycryptodome.hazmat.primitives.asymmetric import kyber from pycryptodome.hazmat.primitives import serialization PQC_AVAILABLE = True except ImportError: PQC_AVAILABLE = False def bytes_to_bits(data: bytes) -> List[int]: return [(byte >> i) & 1 for byte in data for i in range(7, -1, -1)] def bits_to_bytes(bits: List[int]) -> bytes: bits = [b for b in bits if b in (0, 1)] pad_length = (8 - len(bits) % 8) % 8 bits += [0] * pad_length try: return bytes(int(&#39;&#39;.join(map(str, bits[i:i + 8])), 2) for i in range(0, len(bits), 8)) except Exception: return b"" class QuantumProtocol: """基础量子协议类,定义协议接口和通用参数""" def __init__(self): self.security_level = 1 # 1-低, 2-中, 3-高 self.error_threshold = 0.1 # 错误率阈值,超过则视为不安全 def generate_bases(self, length: int) -> List[int]: raise NotImplementedError def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: raise NotImplementedError def get_protocol_params(self) -> Dict: """返回协议参数信息,用于展示和监测""" return { "安全级别": self.security_level, "错误阈值": self.error_threshold } class BB84Protocol(QuantumProtocol): """BB84协议,增加光源强度和测量基选择概率参数""" def __init__(self, light_intensity=0.8, basis_prob=None): super().__init__() self.light_intensity = light_intensity # 光源强度(0-1) self.basis_prob = basis_prob or [0.5, 0.5] # Z基和X基的选择概率 def generate_bases(self, length: int) -> List[int]: return [0 if random.random() < self.basis_prob[0] else 1 for _ in range(length)] def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: matched = [s for s, r, sb, rb in zip(sent_bits, recv_bits, sent_bases, recv_bases) if sb == rb] error_rate = 1 - (sum(1 for s, r in zip(matched[::10], recv_bits[::10]) if s == r) / len(matched[::10])) if matched else 0.0 return matched, min(error_rate, self.error_threshold) def get_protocol_params(self) -> Dict: params = super().get_protocol_params() params.update({ "光源强度": self.light_intensity, "测量基概率(Z/X)": self.basis_prob }) return params class SatelliteQKDProtocol(QuantumProtocol): """卫星QKD协议,增加海拔角度和大气损耗参数""" def __init__(self, elevation_angle=30, weather_factor=1.0): super().__init__() self.elevation_angle = elevation_angle # 海拔角度(度) self.weather_factor = weather_factor # 天气影响因子(1.0为晴天,>1表示恶劣天气) self.atmospheric_loss = 0.1 * (90 / elevation_angle) * weather_factor # 实时计算大气损耗 def generate_bases(self, length: int) -> List[int]: # 低角度时增加基选择随机性以对抗大气干扰 if self.elevation_angle < 20: return [random.randint(0, 1) for _ in range(length)] return [0 if random.random() < 0.6 else 1 for _ in range(length)] # 高角度时偏好Z基 def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: # 考虑大气损耗的匹配机制 loss_resist = [random.random() > self.atmospheric_loss for _ in range(len(sent_bits))] matched = [s for s, r, sb, rb, lr in zip(sent_bits, recv_bits, sent_bases, recv_bases, loss_resist) if sb == rb and lr] # 错误率随海拔角度动态调整 error_rate = 0.04 * (90 / self.elevation_angle) * self.weather_factor if matched else 0.0 return matched[:int(len(matched)*(1-self.atmospheric_loss))], min(error_rate, self.error_threshold) def update_weather(self, weather_factor: float): """实时更新天气状况""" self.weather_factor = weather_factor self.atmospheric_loss = 0.1 * (90 / self.elevation_angle) * weather_factor def get_protocol_params(self) -> Dict: params = super().get_protocol_params() params.update({ "海拔角度(度)": self.elevation_angle, "天气因子": self.weather_factor, "大气损耗": f"{self.atmospheric_loss:.2f}" }) return params class TFQKDProtocol(QuantumProtocol): """时间频率QKD协议,增加频率偏移和同步精度参数""" def __init__(self, frequency_offset=1.2, sync_precision=0.001): super().__init__() self.frequency_offset = frequency_offset # 频率偏移系数 self.sync_precision = sync_precision # 时间同步精度(秒) def generate_bases(self, length: int) -> List[int]: return [random.randint(0, 2) for _ in range(length)] def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: # 频率同步误差导致的匹配损失 sync_loss = int(len(sent_bits) * self.sync_precision * 10) valid_range = len(sent_bits) - sync_loss matched = [s for s, r, sb, rb in zip(sent_bits[:valid_range], recv_bits[sync_loss:], sent_bases[:valid_range], recv_bases[sync_loss:]) if sb == rb == 2] error_rate = 0.02 * self.frequency_offset if matched else 0.0 return matched, min(error_rate, self.error_threshold) def get_protocol_params(self) -> Dict: params = super().get_protocol_params() params.update({ "频率偏移": self.frequency_offset, "同步精度(秒)": self.sync_precision }) return params # 其他协议类保持结构,增加专属参数... class B92Protocol(QuantumProtocol): def __init__(self, encoding_strength=0.9): super().__init__() self.encoding_strength = encoding_strength # 编码强度 def generate_bases(self, length: int) -> List[int]: return [random.randint(0, 1) for _ in range(length)] def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: matched = [s for s, r, sb, rb in zip(sent_bits, recv_bits, sent_bases, recv_bases) if (s == 1 and r == 0) or (s == 0 and r == 1)] # 编码强度影响有效密钥长度 valid_length = int(len(matched) * self.encoding_strength) error_rate = 0.05 if matched else 0.0 return matched[:valid_length], error_rate def get_protocol_params(self) -> Dict: params = super().get_protocol_params() params.update({"编码强度": self.encoding_strength}) return params class E91Protocol(QuantumProtocol): def __init__(self, entanglement_strength=0.85): super().__init__() self.entanglement_strength = entanglement_strength # 纠缠强度 def generate_bases(self, length: int) -> List[int]: return [random.randint(0, 2) for _ in range(length)] def basis_negotiation(self, sent_bits, recv_bits, sent_bases, recv_bases) -> Tuple[List[int], float]: matched = [] for s, r, sb, rb in zip(sent_bits, recv_bits, sent_bases, recv_bases): if sb == rb: matched.append(1 - r) # 纠缠强度影响错误率 error_rate = 0.03 * (1 - self.entanglement_strength + 0.5) if matched else 0.0 return matched, error_rate def get_protocol_params(self) -> Dict: params = super().get_protocol_params() params.update({"纠缠强度": self.entanglement_strength}) return params class KeyPostProcessing: @staticmethod def ldpc_correction(raw_bits: List[int], error_rate: float) -> List[int]: if not raw_bits: return [] correction_window = max(4, int(len(raw_bits) * error_rate * 10)) corrected = raw_bits.copy() for i in range(0, len(corrected), correction_window): window = corrected[i:i + correction_window] if len(window) < 2: continue majority = 1 if sum(window) > len(window) // 2 else 0 for j in range(len(window)): if random.random() < error_rate: window[j] = majority corrected[i:i + correction_window] = window return corrected @staticmethod def privacy_amplification(bits: List[int], seed: Optional[bytes] = None) -> List[int]: if not bits: return [0] * 8 seed = seed or os.urandom(16) bytes_data = bits_to_bytes(bits) hash1 = hashlib.sha3_256(seed + bytes_data).digest() hash2 = hashlib.sha3_512(hash1 + seed).digest() return bytes_to_bits(hash2)[:len(bits)] @dataclass class QuantumChannel: """量子信道类,增加安全监测和警报功能""" distance_km: float loss_rate: float = 0.02 interception_detected: bool = False security_alert_callback: Optional[Callable] = None # 安全警报回调函数 interception_count: int = 0 # 拦截计数 error_history: List[float] = None # 错误率历史记录 def __post_init__(self): self.error_history = self.error_history or [] def transmit(self, sent_bits: List[int], sent_bases: List[int]) -> Tuple[List[int], List[int]]: recv_bases = [random.choice([0, 1]) for _ in range(len(sent_bits))] recv_bits = [] # 模拟传输损耗 for bit in sent_bits: if random.random() < self.loss_rate * self.distance_km / 10: continue recv_bits.append(bit) # 模拟噪声 noise_prob = 0.01 * self.distance_km / 10 for i in range(len(recv_bits)): if random.random() < noise_prob: recv_bits[i] ^= 1 # 检测潜在拦截 interception_prob = 0.02 * self.distance_km / 10 if random.random() < interception_prob: self.interception_detected = True self.interception_count += 1 recv_bits = [random.randint(0, 1) for _ in recv_bits] self._alert_security_monitor() # 触发安全警报 # 记录错误率历史 if sent_bits and recv_bits: error_rate = sum(s != r for s, r in zip(sent_bits[:len(recv_bits)], recv_bits)) / len(recv_bits) self.error_history.append(error_rate) # 保留最近10条记录 if len(self.error_history) > 10: self.error_history.pop(0) return recv_bits, recv_bases def _alert_security_monitor(self): """触发安全警报,通知用户可能存在的拦截""" if self.security_alert_callback: self.security_alert_callback( f"潜在安全威胁:检测到可能的量子拦截!\n" f"拦截次数:{self.interception_count}\n" f"信道距离:{self.distance_km}km" ) else: # 默认警报处理 print(f"[安全警报] 检测到可能的量子拦截!次数: {self.interception_count}") def get_security_status(self) -> Dict: """获取当前信道安全状态""" avg_error = sum(self.error_history) / len(self.error_history) if self.error_history else 0 return { "拦截次数": self.interception_count, "平均错误率": f"{avg_error:.4f}", "最近错误趋势": "上升" if len(self.error_history) >= 3 and self.error_history[-1] > self.error_history[-2] > self.error_history[-3] else "稳定" } class QuantumRandomSource: @staticmethod def get_quantum_random_bits(length: int, use_real_hardware: bool = False, protocol: str = "") -> List[int]: start_time = time.time() batch_size = 16 total_bits = [] if use_real_hardware and PQC_AVAILABLE: try: from qiskit_ibm_provider import IBMProvider provider = IBMProvider() backend = provider.get_backend(&#39;ibmq_qasm_simulator&#39;) except Exception: use_real_hardware = False while len(total_bits) < length: try: if use_real_hardware: circuit = qiskit.QuantumCircuit(batch_size, batch_size) circuit.h(range(batch_size)) circuit.measure(range(batch_size), range(batch_size)) transpiled = qiskit.transpile(circuit, backend) job = backend.run(transpiled, shots=1, memory=True) result = job.result() batch_bits = [int(bit) for bit in result.get_memory()[0]] else: noise_model = NoiseModel() if protocol in ["TF-QKD", "卫星QKD"]: noise_model.add_all_qubit_quantum_error( thermal_relaxation_error(5e3, 2e3, 0.5), [&#39;h&#39;, &#39;measure&#39;] ) else: noise_model.add_all_qubit_quantum_error( depolarizing_error(0.005, 1), [&#39;h&#39;, &#39;measure&#39;] ) simulator = AerSimulator(noise_model=noise_model) circuit = qiskit.QuantumCircuit(batch_size, batch_size) circuit.h(range(batch_size)) circuit.measure(range(batch_size), range(batch_size)) sampler = Sampler(backend=simulator) result = sampler.run(circuits=circuit, shots=1).result() counts = result.quasi_dists[0] bitstring = max(counts, key=counts.get).zfill(batch_size) batch_bits = [int(bit) for bit in bitstring] total_bits.extend(batch_bits) if time.time() - start_time > 5: raise TimeoutError except Exception: total_bits.extend([random.getrandbits(1) for _ in range(batch_size)]) return total_bits[:length] class QuantumEncryptionTool: def __init__( self, protocol: str = "BB84", channel_distance_km: float = 10.0, enable_pqc: bool = False, use_real_quantum: bool = False, security_alert_callback: Optional[Callable] = None ): self.protocol_name = protocol self.protocol = self._get_protocol(protocol) self.channel = QuantumChannel( distance_km=channel_distance_km, security_alert_callback=security_alert_callback ) self.key_processor = KeyPostProcessing() self.hw_signature = self._get_hw_signature() self.use_real_quantum = use_real_quantum self.pqc_private_key = None self.pqc_public_key = None self.authorized_signatures = [self.hw_signature] self._load_authorized_signatures() if enable_pqc and PQC_AVAILABLE: self._init_kyber() else: self.enable_pqc = False def _get_protocol(self, protocol_name: str) -> QuantumProtocol: """根据协议名创建带专属参数的协议实例""" protocols = { "BB84": BB84Protocol(light_intensity=0.85, basis_prob=[0.5, 0.5]), "B92": B92Protocol(encoding_strength=0.9), "E91": E91Protocol(entanglement_strength=0.88), "TF-QKD": TFQKDProtocol(frequency_offset=1.1, sync_precision=0.0008), "卫星QKD": SatelliteQKDProtocol(elevation_angle=45, weather_factor=1.0) } return protocols.get(protocol_name.upper(), BB84Protocol()) def update_protocol_params(self, **kwargs): """更新协议专属参数""" if hasattr(self.protocol, &#39;elevation_angle&#39;) and &#39;elevation_angle&#39; in kwargs: self.protocol.elevation_angle = kwargs[&#39;elevation_angle&#39;] self.protocol.atmospheric_loss = 0.1 * (90 / self.protocol.elevation_angle) * self.protocol.weather_factor if hasattr(self.protocol, &#39;weather_factor&#39;) and &#39;weather_factor&#39; in kwargs: self.protocol.update_weather(kwargs[&#39;weather_factor&#39;]) if hasattr(self.protocol, &#39;light_intensity&#39;) and &#39;light_intensity&#39; in kwargs: self.protocol.light_intensity = kwargs[&#39;light_intensity&#39;] def get_protocol_info(self) -> Dict: """获取当前协议信息和参数""" return { "协议名称": self.protocol_name, "参数": self.protocol.get_protocol_params(), "信道状态": self.channel.get_security_status() } def _get_hw_signature(self) -> bytes: hw_info = [ (psutil.cpu_count(logical=False) or 0).to_bytes(4, &#39;big&#39;), uuid.getnode().to_bytes(6, &#39;big&#39;), platform.machine().encode(), psutil.disk_usage(&#39;/&#39;).total.to_bytes(8, &#39;big&#39;)[:4] ] return hashlib.sha3_256(b&#39;&#39;.join(hw_info)).digest() def _init_kyber(self): self.enable_pqc = True kyber_cipher = kyber.Kyber512() self.pqc_private_key = kyber_cipher.generate_private_key() self.pqc_public_key = self.pqc_private_key.public_key() def _secure_xor(self, data: bytes, key: bytes) -> bytes: key = key.ljust(len(data), b&#39;\x00&#39;)[:len(data)] return bytes(d ^ k for d, k in zip(data, key)) def generate_key(self, data_length: int) -> bytes: start_time = time.time() raw_bit_length = max(data_length * 8 * 2, 128) for attempt in range(3): try: sent_bits = QuantumRandomSource.get_quantum_random_bits( raw_bit_length, use_real_hardware=self.use_real_quantum, protocol=self.protocol_name ) sent_bases = self.protocol.generate_bases(raw_bit_length) recv_bits, recv_bases = self.channel.transmit(sent_bits, sent_bases) matched_bits, error_rate = self.protocol.basis_negotiation( sent_bits, recv_bits, sent_bases, recv_bases ) # 如果错误率超过阈值,视为不安全,重新生成 if error_rate > self.protocol.error_threshold: continue corrected_bits = self.key_processor.ldpc_correction(matched_bits, error_rate) if len(corrected_bits) < data_length * 8: continue final_bits = self.key_processor.privacy_amplification(corrected_bits) final_key = bits_to_bytes(final_bits)[:data_length] if len(final_key) == data_length: return final_key except Exception: continue return os.urandom(data_length) # 以下方法保持原有功能,省略... def encrypt_file(self, file_path: str, progress_callback=None) -> Tuple[bytes, bytes, Optional[bytes]]: chunk_size = 4096 session_key = self.generate_key(chunk_size) pqc_encapsulated = None if self.enable_pqc: pqc_encapsulated, _ = self.pqc_public_key.encrypt(session_key) ciphertext_chunks = [] file_size = os.path.getsize(file_path) processed = 0 with open(file_path, &#39;rb&#39;) as f: while True: chunk = f.read(chunk_size) if not chunk: break ciphertext_chunks.append(self._secure_xor(chunk, session_key)) processed += len(chunk) if progress_callback: progress_callback(min(100, int(processed / file_size * 100))) ciphertext = b&#39;&#39;.join(ciphertext_chunks) signed_key = session_key + self.hw_signature return ciphertext, signed_key, pqc_encapsulated def decrypt_file(self, ciphertext: bytes, signed_key: bytes, output_path: str, pqc_encapsulated: Optional[bytes] = None, progress_callback=None) -> bool: if len(signed_key) < len(ciphertext) + len(self.hw_signature): return False session_key = signed_key[:len(ciphertext)] stored_signature = signed_key[-len(self.hw_signature):] if stored_signature not in self.authorized_signatures: return False if self.enable_pqc and pqc_encapsulated: try: session_key = self.pqc_private_key.decrypt(pqc_encapsulated) except Exception: return False chunk_size = 4096 total_chunks = (len(ciphertext) + chunk_size - 1) // chunk_size processed = 0 with open(output_path, &#39;wb&#39;) as f: for i in range(0, len(ciphertext), chunk_size): chunk = ciphertext[i:i + chunk_size] plaintext_chunk = self._secure_xor(chunk, session_key) f.write(plaintext_chunk) processed += 1 if progress_callback: progress_callback(min(100, int(processed / total_chunks * 100))) return True def encrypt_text(self, plaintext: str) -> Tuple[bytes, bytes, Optional[bytes]]: plaintext_bytes = plaintext.encode(&#39;utf-8&#39;) session_key = self.generate_key(len(plaintext_bytes)) ciphertext = self._secure_xor(plaintext_bytes, session_key) signed_key = session_key + self.hw_signature pqc_encapsulated = None if self.enable_pqc: pqc_encapsulated, _ = self.pqc_public_key.encrypt(session_key) return ciphertext, signed_key, pqc_encapsulated def decrypt_text(self, ciphertext: bytes, signed_key: bytes, pqc_encapsulated: Optional[bytes] = None) -> str: if len(signed_key) < len(ciphertext) + len(self.hw_signature): raise ValueError("密钥长度不足") session_key = signed_key[:len(ciphertext)] stored_signature = signed_key[-len(self.hw_signature):] if stored_signature not in self.authorized_signatures: raise PermissionError("设备未授权") if self.enable_pqc and pqc_encapsulated: try: session_key = self.pqc_private_key.decrypt(pqc_encapsulated) except Exception: raise ValueError("PQC解密失败") plaintext_bytes = self._secure_xor(ciphertext, session_key) return plaintext_bytes.decode(&#39;utf-8&#39;, errors=&#39;replace&#39;) def _load_authorized_signatures(self): if os.path.exists("authorized_devices.txt"): with open("authorized_devices.txt", "r") as f: self.authorized_signatures = [bytes.fromhex(line.strip()) for line in f if line.strip()] def _save_authorized_signatures(self): with open("authorized_devices.txt", "w") as f: for sig in self.authorized_signatures: f.write(sig.hex() + "\n") def get_authorized_devices(self) -> List[str]: return [sig.hex()[:16] + "..." for sig in self.authorized_signatures] def revoke_authorization(self, sig_hex: str) -> bool: full_sig = None for sig in self.authorized_signatures: if sig.hex().startswith(sig_hex): full_sig = sig break if full_sig and full_sig != self.hw_signature: self.authorized_signatures.remove(full_sig) self._save_authorized_signatures() return True return False def generate_authorization_code(self) -> str: timestamp = int(time.time()).to_bytes(8, &#39;big&#39;) data = timestamp + self.hw_signature if self.enable_pqc and self.pqc_public_key: encrypted_data = self.pqc_public_key.encrypt(data) else: encrypted_data = data return encrypted_data.hex() def add_authorized_device(self, auth_code: str) -> bool: try: data = bytes.fromhex(auth_code) if self.enable_pqc and self.pqc_private_key: data = self.pqc_private_key.decrypt(data) timestamp = int.from_bytes(data[:8], &#39;big&#39;) if time.time() - timestamp > 600: return False new_sig = data[8:] if new_sig not in self.authorized_signatures: self.authorized_signatures.append(new_sig) self._save_authorized_signatures() return True return False except Exception: return False class EncryptionGUI: def __init__(self, root): self.root = root self.root.title("量子加密工具 v3.2") self.root.geometry("1000x700") self.root.resizable(True, True) self.font_config() self.encryption_core = None self._init_core() self.tab_control = ttk.Notebook(root) self.tab_encrypt = ttk.Frame(self.tab_control) self.tab_decrypt = ttk.Frame(self.tab_control) self.tab_settings = ttk.Frame(self.tab_control) self.tab_authorization = ttk.Frame(self.tab_control) self.tab_monitor = ttk.Frame(self.tab_control) # 新增安全监测标签页 self.tab_control.add(self.tab_encrypt, text="加密") self.tab_control.add(self.tab_decrypt, text="解密") self.tab_control.add(self.tab_settings, text="设置") self.tab_control.add(self.tab_authorization, text="授权管理") self.tab_control.add(self.tab_monitor, text="安全监测") # 添加到标签页 self.tab_control.pack(expand=1, fill="both") self._setup_encrypt_tab() self._setup_decrypt_tab() self._setup_settings_tab() self._setup_authorization_tab() self._setup_monitor_tab() # 初始化监测页面 self._add_help_text() def font_config(self): default_font = (&#39;SimHei&#39;, 10) self.root.option_add("*Font", default_font) def _init_core(self): # 传递安全警报回调函数 self.encryption_core = QuantumEncryptionTool( protocol="BB84", channel_distance_km=10.0, enable_pqc=False, use_real_quantum=False, security_alert_callback=self._show_security_alert ) def _show_security_alert(self, message: str): """显示安全警报对话框""" self.root.after(0, lambda: messagebox.showerror("安全警报", message)) # 更新监测页面 self.root.after(0, self._update_monitor_data) def _setup_monitor_tab(self): """安全监测页面,展示信道状态和协议参数""" frame = ttk.Frame(self.tab_monitor, padding=10) frame.pack(fill="both", expand=True) # 信道安全状态 ttk.Label(frame, text="信道安全状态", font=(&#39;SimHei&#39;, 12, &#39;bold&#39;)).pack(anchor="w", pady=10) self.security_status_tree = ttk.Treeview(frame, columns=["指标", "值"], show="headings", height=5) self.security_status_tree.heading("指标", text="安全指标") self.security_status_tree.heading("值", text="当前状态") self.security_status_tree.column("指标", width=200) self.security_status_tree.column("值", width=300) self.security_status_tree.pack(fill="x", pady=5) # 协议参数 ttk.Label(frame, text="当前协议参数", font=(&#39;SimHei&#39;, 12, &#39;bold&#39;)).pack(anchor="w", pady=10) self.protocol_params_tree = ttk.Treeview(frame, columns=["参数", "值"], show="headings", height=8) self.protocol_params_tree.heading("参数", text="协议参数") self.protocol_params_tree.heading("值", text="参数值") self.protocol_params_tree.column("参数", width=200) self.protocol_params_tree.column("值", width=300) self.protocol_params_tree.pack(fill="x", pady=5) # 刷新按钮 ttk.Button(frame, text="刷新状态", command=self._update_monitor_data).pack(pady=10) # 初始更新数据 self._update_monitor_data() def _update_monitor_data(self): """更新监测页面数据""" # 清空现有数据 for item in self.security_status_tree.get_children(): self.security_status_tree.delete(item) for item in self.protocol_params_tree.get_children(): self.protocol_params_tree.delete(item) # 获取协议和信道信息 info = self.encryption_core.get_protocol_info() # 更新安全状态 for key, value in info["信道状态"].items(): self.security_status_tree.insert("", "end", values=[key, value]) # 更新协议参数 self.protocol_params_tree.insert("", "end", values=["协议名称", info["协议名称"]]) for key, value in info["参数"].items(): self.protocol_params_tree.insert("", "end", values=[key, value]) # 其他UI设置方法保持不变,增加协议参数配置区域... def _setup_settings_tab(self): frame = ttk.Frame(self.tab_settings, padding=10) frame.pack(fill="both", expand=True) # 协议选择 ttk.Label(frame, text="选择量子协议:", font=(&#39;SimHei&#39;, 11)).pack(anchor="w", pady=(10, 0)) self.protocol_var = tk.StringVar(value="BB84") protocol_frame = ttk.Frame(frame) protocol_frame.pack(fill="x", pady=5) protocols = ["BB84", "B92", "E91", "TF-QKD", "卫星QKD"] for proto in protocols: ttk.Radiobutton( protocol_frame, text=proto, variable=self.protocol_var, value=proto, command=self._update_protocol_params_ui # 切换协议时更新参数UI ).pack(side="left", padx=10) # 协议专属参数配置区域 self.protocol_params_frame = ttk.LabelFrame(frame, text="协议专属参数") self.protocol_params_frame.pack(fill="x", pady=10, padx=5) self._update_protocol_params_ui() # 初始加载参数UI # 信道设置 ttk.Label(frame, text="模拟信道距离(km):", font=(&#39;SimHei&#39;, 11)).pack(anchor="w", pady=(10, 0)) self.distance_var = tk.DoubleVar(value=10.0) ttk.Scale( frame, from_=1.0, to=100.0, variable=self.distance_var, command=lambda v: self._update_distance_label(v) ).pack(fill="x", pady=5) self.distance_label = ttk.Label(frame, text="10.0 km") self.distance_label.pack(anchor="w") # 其他设置 self.pqc_var = tk.BooleanVar(value=False) ttk.Checkbutton( frame, text="启用后量子加密(抗未来量子计算机攻击)", variable=self.pqc_var ).pack(anchor="w", pady=10) self.real_quantum_var = tk.BooleanVar(value=False) ttk.Checkbutton( frame, text="使用真实量子硬件(需IBM账号)", variable=self.real_quantum_var ).pack(anchor="w", pady=5) ttk.Label( frame, text="提示:真实硬件可能较慢,默认使用模拟器", foreground="gray" ).pack(anchor="w") ttk.Button(frame, text="应用设置", command=self._apply_settings).pack(pady=20) def _update_protocol_params_ui(self): """根据当前选择的协议更新参数配置UI""" # 清空现有参数控件 for widget in self.protocol_params_frame.winfo_children(): widget.destroy() protocol = self.protocol_var.get() # 为不同协议创建专属参数控件 if protocol == "卫星QKD": ttk.Label(self.protocol_params_frame, text="海拔角度(度):").pack(anchor="w", pady=5) self.elevation_angle_var = tk.IntVar(value=45) angle_frame = ttk.Frame(self.protocol_params_frame) angle_frame.pack(fill="x", padx=5) ttk.Scale( angle_frame, from_=5, to=90, variable=self.elevation_angle_var, command=lambda v: ttk.Label(angle_frame, text=f"{int(float(v))} 度").pack(side="right") ).pack(side="left", fill="x", expand=True) ttk.Label(self.protocol_params_frame, text="天气因子(1.0为晴天):").pack(anchor="w", pady=5) self.weather_factor_var = tk.DoubleVar(value=1.0) weather_frame = ttk.Frame(self.protocol_params_frame) weather_frame.pack(fill="x", padx=5) ttk.Scale( weather_frame, from_=1.0, to=3.0, variable=self.weather_factor_var, command=lambda v: ttk.Label(weather_frame, text=f"{float(v):.1f}").pack(side="right") ).pack(side="left", fill="x", expand=True) elif protocol == "BB84": ttk.Label(self.protocol_params_frame, text="光源强度(0-1):").pack(anchor="w", pady=5) self.light_intensity_var = tk.DoubleVar(value=0.85) light_frame = ttk.Frame(self.protocol_params_frame) light_frame.pack(fill="x", padx=5) ttk.Scale( light_frame, from_=0.5, to=1.0, variable=self.light_intensity_var, command=lambda v: ttk.Label(light_frame, text=f"{float(v):.2f}").pack(side="right") ).pack(side="left", fill="x", expand=True) elif protocol == "TF-QKD": ttk.Label(self.protocol_params_frame, text="频率偏移系数:").pack(anchor="w", pady=5) self.frequency_offset_var = tk.DoubleVar(value=1.1) freq_frame = ttk.Frame(self.protocol_params_frame) freq_frame.pack(fill="x", padx=5) ttk.Scale( freq_frame, from_=1.0, to=2.0, variable=self.frequency_offset_var, command=lambda v: ttk.Label(freq_frame, text=f"{float(v):.2f}").pack(side="right") ).pack(side="left", fill="x", expand=True) # 没有专属参数的协议显示提示 if protocol not in ["卫星QKD", "BB84", "TF-QKD"]: ttk.Label( self.protocol_params_frame, text="该协议使用默认参数配置", foreground="gray" ).pack(pady=10) def _apply_settings(self): try: # 创建加密核心实例 self.encryption_core = QuantumEncryptionTool( protocol=self.protocol_var.get(), channel_distance_km=self.distance_var.get(), enable_pqc=self.pqc_var.get(), use_real_quantum=self.real_quantum_var.get(), security_alert_callback=self._show_security_alert ) # 更新协议专属参数 protocol = self.protocol_var.get() if protocol == "卫星QKD": self.encryption_core.update_protocol_params( elevation_angle=self.elevation_angle_var.get(), weather_factor=self.weather_factor_var.get() ) elif protocol == "BB84": self.encryption_core.update_protocol_params( light_intensity=self.light_intensity_var.get() ) elif protocol == "TF-QKD": self.encryption_core.update_protocol_params( frequency_offset=self.frequency_offset_var.get() ) self._refresh_auth_list() self._update_monitor_data() # 刷新监测数据 messagebox.showinfo("成功", "设置已应用") except Exception as e: messagebox.showerror("错误", f"设置应用失败:{e}") # 其他UI方法保持不变... def _setup_encrypt_tab(self): frame = ttk.Frame(self.tab_encrypt, padding=10) frame.pack(fill="both", expand=True) ttk.Label(frame, text="文本加密:").pack(anchor="w", pady=(5, 0)) self.encrypt_text_input = tk.Text(frame, height=5, wrap=tk.WORD) self.encrypt_text_input.pack(fill="both", expand=True, pady=5) ttk.Label(frame, text="文件加密:").pack(anchor="w", pady=(10, 0)) file_frame = ttk.Frame(frame) file_frame.pack(fill="x", pady=5) self.encrypt_file_path = tk.StringVar() entry = ttk.Entry(file_frame, textvariable=self.encrypt_file_path) entry.pack(side="left", fill="x", expand=True, padx=5) entry.bind("<Button-1>", lambda e: entry.focus_set()) ttk.Button(file_frame, text="选择文件", command=self._select_encrypt_file).pack(side="left", padx=5) progress_frame = ttk.Frame(frame) progress_frame.pack(fill="x", pady=5) self.encrypt_progress = ttk.Progressbar(progress_frame, orient="horizontal", length=100, mode="determinate") self.encrypt_progress.pack(side="left", fill="x", expand=True, padx=5) self.encrypt_status = ttk.Label(progress_frame, text="就绪") self.encrypt_status.pack(side="left", padx=5) btn_frame = ttk.Frame(frame) btn_frame.pack(fill="x", pady=5) ttk.Button(btn_frame, text="加密文本", command=self._start_text_encrypt).pack(side="left", padx=5) ttk.Button(btn_frame, text="加密文件", command=self._start_file_encrypt).pack(side="left", padx=5) ttk.Label(frame, text="加密结果(密文):").pack(anchor="w", pady=(10, 0)) self.ciphertext_output = tk.Text(frame, height=5, wrap=tk.WORD) self.ciphertext_output.pack(fill="both", expand=True, pady=5) ttk.Label(frame, text="带签名密钥(请保存):").pack(anchor="w", pady=(10, 0)) self.key_output = tk.Text(frame, height=5, wrap=tk.WORD) self.key_output.pack(fill="both", expand=True, pady=5) def _setup_decrypt_tab(self): frame = ttk.Frame(self.tab_decrypt, padding=10) frame.pack(fill="both", expand=True) ttk.Label(frame, text="文本解密:").pack(anchor="w", pady=(5, 0)) ttk.Label(frame, text="密文:").pack(anchor="w") self.decrypt_cipher_input = tk.Text(frame, height=5, wrap=tk.WORD) self.decrypt_cipher_input.pack(fill="both", expand=True, pady=5) ttk.Label(frame, text="带签名密钥:").pack(anchor="w") self.decrypt_key_input = tk.Text(frame, height=5, wrap=tk.WORD) self.decrypt_key_input.pack(fill="both", expand=True, pady=5) ttk.Label(frame, text="文件解密:").pack(anchor="w", pady=(10, 0)) file_frame = ttk.Frame(frame) file_frame.pack(fill="x", pady=5) self.decrypt_file_path = tk.StringVar() entry = ttk.Entry(file_frame, textvariable=self.decrypt_file_path) entry.pack(side="left", fill="x", expand=True, padx=5) entry.bind("<Button-1>", lambda e: entry.focus_set()) ttk.Button(file_frame, text="选择密文文件", command=self._select_decrypt_file).pack(side="left", padx=5) progress_frame = ttk.Frame(frame) progress_frame.pack(fill="x", pady=5) self.decrypt_progress = ttk.Progressbar(progress_frame, orient="horizontal", length=100, mode="determinate") self.decrypt_progress.pack(side="left", fill="x", expand=True, padx=5) self.decrypt_status = ttk.Label(progress_frame, text="就绪") self.decrypt_status.pack(side="left", padx=5) btn_frame = ttk.Frame(frame) btn_frame.pack(fill="x", pady=5) ttk.Button(btn_frame, text="解密文本", command=self._start_text_decrypt).pack(side="left", padx=5) ttk.Button(btn_frame, text="解密文件", command=self._start_file_decrypt).pack(side="left", padx=5) ttk.Label(frame, text="解密结果:").pack(anchor="w", pady=(10, 0)) self.decrypt_output = tk.Text(frame, height=5, wrap=tk.WORD) self.decrypt_output.pack(fill="both", expand=True, pady=5) def _setup_authorization_tab(self): frame = ttk.Frame(self.tab_authorization, padding=10) frame.pack(fill="both", expand=True) ttk.Label(frame, text="已授权设备列表:").pack(anchor="w", pady=(10, 0)) self.auth_listbox = tk.Listbox(frame, height=10) self.auth_listbox.pack(fill="both", expand=True, pady=5) self._refresh_auth_list() btn_frame = ttk.Frame(frame) btn_frame.pack(fill="x", pady=10) ttk.Button(btn_frame, text="生成授权码", command=self._generate_auth_code).pack(side="left", padx=5) ttk.Button(btn_frame, text="导入授权码", command=self._import_auth_code).pack(side="left", padx=5) ttk.Button(btn_frame, text="撤销选中授权", command=self._revoke_selected).pack(side="right", padx=5) ttk.Label(frame, text="提示:当前设备授权无法撤销", foreground="gray").pack(anchor="w", pady=5) def _refresh_auth_list(self): self.auth_listbox.delete(0, tk.END) devices = self.encryption_core.get_authorized_devices() for i, device in enumerate(devices): label = f"设备 {i + 1}: {device}" if i == 0: label += "(当前设备)" self.auth_listbox.insert(tk.END, label) def _add_help_text(self): help_text = """ 操作指南: 1. 加密:支持文本/文件加密,文件加密会显示进度条 2. 解密:对应输入密文/选择密文文件,输入密钥即可解密 3. 协议:不同协议有专属参数,可在设置页调整 4. 安全监测:实时查看信道状态和潜在威胁警报 5. 授权:在"授权管理"中管理设备授权 注意:检测到拦截时会触发安全警报 """ ttk.Label( self.tab_encrypt, text=help_text, foreground="gray", justify="left" ).pack(side="bottom", fill="x", pady=10) def _update_distance_label(self, value): self.distance_label.config(text=f"{float(value):.1f} km") def _select_encrypt_file(self): file_path = filedialog.askopenfilename() if file_path and os.path.exists(file_path): self.encrypt_file_path.set(file_path) self.encrypt_status.config(text=f"已选择: {os.path.basename(file_path)}") else: self.encrypt_status.config(text="未选择有效文件") def _select_decrypt_file(self): file_path = filedialog.askopenfilename() if file_path and os.path.exists(file_path): self.decrypt_file_path.set(file_path) self.decrypt_status.config(text=f"已选择: {os.path.basename(file_path)}") else: self.decrypt_status.config(text="未选择有效文件") def _update_encrypt_progress(self, value): self.encrypt_progress["value"] = value self.encrypt_status.config(text=f"处理中: {int(value)}%") self.root.update_idletasks() def _update_decrypt_progress(self, value): self.decrypt_progress["value"] = value self.decrypt_status.config(text=f"处理中: {int(value)}%") self.root.update_idletasks() def _start_text_encrypt(self): plaintext = self.encrypt_text_input.get(1.0, tk.END).strip() if not plaintext: messagebox.showwarning("警告", "请输入要加密的文本") return self.ciphertext_output.delete(1.0, tk.END) self.key_output.delete(1.0, tk.END) self.encrypt_status.config(text="加密中...") def encrypt_thread(): try: ciphertext, signed_key, pqc_data = self.encryption_core.encrypt_text(plaintext) self.root.after(0, lambda: self.ciphertext_output.insert(tk.END, ciphertext.hex())) self.root.after(0, lambda: self.key_output.insert(tk.END, signed_key.hex())) self.root.after(0, lambda: self.encrypt_status.config(text="加密完成")) self.root.after(0, lambda: messagebox.showinfo("成功", "文本加密完成,请保存密文和密钥")) except Exception as e: self.root.after(0, lambda: self.encrypt_status.config(text="加密失败")) self.root.after(0, lambda: messagebox.showerror("错误", f"加密失败:{str(e)}")) threading.Thread(target=encrypt_thread, daemon=True).start() def _start_file_encrypt(self): file_path = self.encrypt_file_path.get() if not file_path or not os.path.exists(file_path): messagebox.showwarning("警告", "请选择有效的文件") return self.encrypt_progress["value"] = 0 self.encrypt_status.config(text="准备加密...") self.ciphertext_output.delete(1.0, tk.END) self.key_output.delete(1.0, tk.END) def encrypt_thread(): try: ciphertext, signed_key, pqc_data = self.encryption_core.encrypt_file( file_path, progress_callback=self._update_encrypt_progress ) def save_file(): save_path = filedialog.asksaveasfilename(defaultextension=".enc") if save_path: with open(save_path, &#39;wb&#39;) as f: f.write(ciphertext) self.key_output.insert(tk.END, signed_key.hex()) self.encrypt_status.config(text="加密完成") messagebox.showinfo( "成功", f"文件加密完成,密文已保存到:\n{save_path}\n请保存密钥" ) else: self.encrypt_status.config(text="已取消保存") self.root.after(0, save_file) except Exception as e: self.root.after(0, lambda: self.encrypt_status.config(text="加密失败")) self.root.after(0, lambda: messagebox.showerror("错误", f"文件加密失败:{str(e)}")) finally: self.root.after(0, lambda: self.encrypt_progress.config(value=0)) threading.Thread(target=encrypt_thread, daemon=True).start() def _start_text_decrypt(self): ciphertext_hex = self.decrypt_cipher_input.get(1.0, tk.END).strip() key_hex = self.decrypt_key_input.get(1.0, tk.END).strip() if not ciphertext_hex or not key_hex: messagebox.showwarning("警告", "请输入密文和密钥") return try: ciphertext = bytes.fromhex(ciphertext_hex) signed_key = bytes.fromhex(key_hex) except ValueError: messagebox.showerror("错误", "密文或密钥格式错误(请输入16进制字符串)") return self.decrypt_status.config(text="解密中...") self.decrypt_output.delete(1.0, tk.END) def decrypt_thread(): try: decrypted = self.encryption_core.decrypt_text(ciphertext, signed_key) self.root.after(0, lambda: self.decrypt_output.insert(tk.END, decrypted)) self.root.after(0, lambda: self.decrypt_status.config(text="解密完成")) except PermissionError: self.root.after(0, lambda: self.decrypt_status.config(text="解密失败")) self.root.after(0, lambda: messagebox.showerror("错误", "设备未授权,无法解密(请导入授权码)")) except Exception as e: self.root.after(0, lambda: self.decrypt_status.config(text="解密失败")) self.root.after(0, lambda: messagebox.showerror("错误", f"解密失败:{str(e)}")) threading.Thread(target=decrypt_thread, daemon=True).start() def _start_file_decrypt(self): file_path = self.decrypt_file_path.get() key_hex = self.decrypt_key_input.get(1.0, tk.END).strip() if not file_path or not os.path.exists(file_path) or not key_hex: messagebox.showwarning("警告", "请选择密文文件并输入密钥") return try: with open(file_path, &#39;rb&#39;) as f: ciphertext = f.read() signed_key = bytes.fromhex(key_hex) except ValueError: messagebox.showerror("错误", "密钥格式错误(请输入16进制字符串)") return except Exception as e: messagebox.showerror("错误", f"读取文件失败:{e}") return self.decrypt_progress["value"] = 0 self.decrypt_status.config(text="准备解密...") def decrypt_thread(): try: def save_file(): save_path = filedialog.asksaveasfilename() if not save_path: self.root.after(0, lambda: self.decrypt_status.config(text="已取消保存")) return success = self.encryption_core.decrypt_file( ciphertext, signed_key, save_path, progress_callback=self._update_decrypt_progress ) if success: self.root.after(0, lambda: self.decrypt_status.config(text="解密完成")) self.root.after(0, lambda: messagebox.showinfo("成功", f"文件解密完成,已保存到:\n{save_path}")) else: self.root.after(0, lambda: self.decrypt_status.config(text="解密失败")) self.root.after(0, lambda: messagebox.showerror("错误", "解密失败(密钥无效或设备未授权)")) self.root.after(0, save_file) except Exception as e: self.root.after(0, lambda: self.decrypt_status.config(text="解密失败")) self.root.after(0, lambda: messagebox.showerror("错误", f"文件解密失败:{e}")) finally: self.root.after(0, lambda: self.decrypt_progress.config(value=0)) threading.Thread(target=decrypt_thread, daemon=True).start() def _generate_auth_code(self): try: code = self.encryption_core.generate_authorization_code() messagebox.showinfo("授权码", f"此授权码10分钟内有效,请发送给需要授权的设备:\n{code}") except Exception as e: messagebox.showerror("错误", f"生成授权码失败:{e}") def _import_auth_code(self): code = simpledialog.askstring("导入授权码", "请输入授权码:") if code and self.encryption_core.add_authorized_device(code): self._refresh_auth_list() messagebox.showinfo("成功", "设备已授权,可解密对应内容") else: messagebox.showerror("错误", "授权码无效或已过期") def _revoke_selected(self): selected = self.auth_listbox.curselection() if not selected: messagebox.showwarning("警告", "请选择要撤销的设备") return index = selected[0] if index == 0: messagebox.showwarning("警告", "当前设备授权无法撤销") return device_text = self.auth_listbox.get(index) sig_hex = device_text.split(": ")[1].split("...")[0] if self.encryption_core.revoke_authorization(sig_hex): self._refresh_auth_list() messagebox.showinfo("成功", "已撤销选中设备的授权") else: messagebox.showerror("错误", "撤销授权失败") if __name__ == "__main__": try: import sys if len(sys.argv) > 1 and sys.argv[1] == "--cli": tool = QuantumEncryptionTool(protocol="BB84", enable_pqc=False) secret = input("请输入要加密的内容:") ciphertext, signed_key, pqc_data = tool.encrypt_text(secret) print(f"\n密文:{ciphertext.hex()}") print(f"带签名密钥:{signed_key.hex()}") decrypt_choice = input("\n是否立即解密?(y/n):") if decrypt_choice.lower() == &#39;y&#39;: decrypted = tool.decrypt_text(ciphertext, signed_key, pqc_data) print(f"\n解密结果:{decrypted}") else: root = tk.Tk() app = EncryptionGUI(root) root.mainloop() except Exception as e: print(f"程序运行失败:{e}")
10-21
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值