劫持微信聊天记录并分析还原 —— 合并解密后的数据库(三)


本工具设计的初衷是用来获取微信账号的相关信息并解析PC版微信的数据库。 

        程序以 Python 语言开发,可读取、解密、还原微信数据库并帮助用户查看聊天记录,还可以将其聊天记录导出为csv、html等格式用于AI训练,自动回复或备份等等作用。下面我们将深入探讨这个工具的各个方面及其工作原理。 

本项目仅供学习交流使用,严禁用于商业用途或非法途径,任何违反法律法规、侵犯他人合法权益的行为,均与本项目及其开发者无关,后果由行为人自行承担。 


【完整演示工具下载】 

https://download.csdn.net/download/qq_39190622/89958183icon-default.png?t=O83Ahttps://download.csdn.net/download/qq_39190622/89958183


        我们接着上一篇文章《劫持微信聊天记录并分析还原 —— 解密数据库(二)》将解密后的微信数据库合并为一整个DB文件。

微信数据库目录
微信数据库存放目录 
解密后的微信数据库目录
解密后的微信数据库

详细命令:

merge -i "C:\Users\admin\AppData\Local\Temp\wx_tmp" -o "C:\Users\admin\AppData\Local\Temp\wxdb_all.db"

-i 为解密后微信数据库的存放路径

-o 为合并微信数据库的存放路径与文件名

运行命令后,我们可以看到在此位置 "C:\Users\admin\AppData\Local\Temp"  下生成了一个以 “wxdb_all.db” 命名的文件,这便是解密后的微信数据库合并成功的文件,下一步我们将详细解读微信数据库的结构以及如何打开并访问里面的内容。


部分现实代码:

# -*- coding: utf-8 -*-#
# -------------------------------------------------------------------------------
# Name:         merge_db.py
# Description:  
# Author:       Rainbow(www.chwm.vip)
# Date:         2024/11/08
# -------------------------------------------------------------------------------
import logging
import os
import shutil
import sqlite3
import subprocess
import time
from typing import Listfrom .decryption import batch_decrypt
from .wx_info import get_core_db
from .utils import wx_core_loger, wx_core_error, CORE_DB_TYPE@wx_core_error
def execute_sql(connection, sql, params=None):"""执行给定的SQL语句,返回结果。参数:- connection: SQLite连接- sql:要执行的SQL语句- params:SQL语句中的参数"""try:# connection.text_factory = bytescursor = connection.cursor()if params:cursor.execute(sql, params)else:cursor.execute(sql)return cursor.fetchall()except Exception as e:try:connection.text_factory = bytescursor = connection.cursor()if params:cursor.execute(sql, params)else:cursor.execute(sql)rdata = cursor.fetchall()connection.text_factory = strreturn rdataexcept Exception as e:wx_core_loger.error(f"**********\nSQL: {sql}\nparams: {params}\n{e}\n**********", exc_info=True)return None@wx_core_error
def check_create_sync_log(connection):"""检查是否存在表 sync_log,用于记录同步记录,包括微信数据库路径,表名,记录数,同步时间:param connection: SQLite连接:return: True or False"""out_cursor = connection.cursor()# 检查是否存在表 sync_log,用于记录同步记录,包括微信数据库路径,表名,记录数,同步时间sync_log_status = execute_sql(connection, "SELECT name FROM sqlite_master WHERE type='table' AND name='sync_log'")if len(sync_log_status) < 1:#  db_path 微信数据库路径,tbl_name 表名,src_count 源数据库记录数,current_count 当前合并后的数据库对应表记录数sync_record_create_sql = ("CREATE TABLE sync_log (""id INTEGER PRIMARY KEY AUTOINCREMENT,""db_path TEXT NOT NULL,""tbl_name TEXT NOT NULL,""src_count INT,""current_count INT,""createTime INT DEFAULT (strftime('%s', 'now')), ""updateTime INT DEFAULT (strftime('%s', 'now'))"");")out_cursor.execute(sync_record_create_sql)# 创建索引out_cursor.execute("CREATE INDEX idx_sync_log_db_path ON sync_log (db_path);")out_cursor.execute("CREATE INDEX idx_sync_log_tbl_name ON sync_log (tbl_name);")# 创建联合索引,防止重复out_cursor.execute("CREATE UNIQUE INDEX idx_sync_log_db_tbl ON sync_log (db_path, tbl_name);")connection.commit()out_cursor.close()return True@wx_core_error
def check_create_file_md5(connection):"""检查是否存在表 file_md5,用于记录文件信息,后续用于去重等操作,暂时闲置"""pass@wx_core_error
def merge_db(db_paths: List[dict], save_path: str = "merge.db", is_merge_data: bool = True,startCreateTime: int = 0, endCreateTime: int = 0):"""合并数据库 会忽略主键以及重复的行。:param db_paths: [{"db_path": "xxx", "de_path": "xxx"},...]db_path表示初始路径,de_path表示解密后的路径;初始路径用于保存合并的日志情况,解密后的路径用于读取数据:param save_path: str 输出文件路径:param is_merge_data: bool 是否合并数据(如果为False,则只解密,并创建表,不插入数据):param startCreateTime: 开始时间戳 主要用于MSG数据库的合并:param endCreateTime:  结束时间戳 主要用于MSG数据库的合并:return:"""if os.path.isdir(save_path):save_path = os.path.join(save_path, f"merge_{int(time.time())}.db")if isinstance(db_paths, list):# alias, file_pathdatabases = {f"dbi_{i}": (db['db_path'],db.get('de_path', db['db_path'])) for i, db in enumerate(db_paths)}else:raise TypeError("db_paths 类型错误")outdb = sqlite3.connect(save_path)is_sync_log = check_create_sync_log(outdb)if not is_sync_log:wx_core_loger.warning("创建同步记录表失败")out_cursor = outdb.cursor()# 将MSG_db_paths中的数据合并到out_db_path中for alias, db in databases.items():db_path = db[0]de_path = db[1]# 附加数据库sql_attach = f"ATTACH DATABASE '{de_path}' AS {alias}"out_cursor.execute(sql_attach)outdb.commit()sql_query_tbl_name = f"SELECT tbl_name, sql FROM {alias}.sqlite_master WHERE type='table' ORDER BY tbl_name;"tables = execute_sql(outdb, sql_query_tbl_name)for table in tables:table, init_create_sql = table[0], table[1]table = table if isinstance(table, str) else table.decode()init_create_sql = init_create_sql if isinstance(init_create_sql, str) else init_create_sql.decode()if table == "sqlite_sequence":continueif "CREATE TABLE".lower() not in str(init_create_sql).lower():continue# 获取表中的字段名sql_query_columns = f"PRAGMA table_info({table})"columns = execute_sql(outdb, sql_query_columns)if table == "ChatInfo" and len(columns) > 12:  # bizChat中的ChatInfo表与MicroMsg中的ChatInfo表字段不同continuecol_type = {(i[1] if isinstance(i[1], str) else i[1].decode(),i[2] if isinstance(i[2], str) else i[2].decode())for i in columns}columns = [i[0] for i in col_type]if not columns or len(columns) < 1:continue# 创建表tablesql_create_tbl = f"CREATE TABLE IF NOT EXISTS {table} AS SELECT *  FROM {alias}.{table} WHERE 0 = 1;"out_cursor.execute(sql_create_tbl)# 创建包含 NULL 值比较的 UNIQUE 索引index_name = f"{table}_unique_index"coalesce_columns = ','.join(f"COALESCE({column}, '')" for column in columns)sql = f"CREATE UNIQUE INDEX IF NOT EXISTS {index_name} ON {table} ({coalesce_columns})"out_cursor.execute(sql)# 插入sync_logsql_query_sync_log = f"SELECT src_count FROM sync_log WHERE db_path=? AND tbl_name=?"sync_log = execute_sql(outdb, sql_query_sync_log, (db_path, table))if not sync_log or len(sync_log) < 1:sql_insert_sync_log = "INSERT INTO sync_log (db_path, tbl_name, src_count, current_count) VALUES (?, ?, ?, ?)"out_cursor.execute(sql_insert_sync_log, (db_path, table, 0, 0))outdb.commit()if is_merge_data:# 比较源数据库和合并后的数据库记录数log_src_count = execute_sql(outdb, sql_query_sync_log, (db_path, table))[0][0]src_count = execute_sql(outdb, f"SELECT COUNT(*) FROM {alias}.{table}")[0][0]if src_count <= log_src_count:wx_core_loger.info(f"忽略 {db_path} {de_path} {table} {src_count} {log_src_count}")continue# 构建数据查询sqlsql_base = f"SELECT {','.join([i for i in columns])} FROM {alias}.{table} "where_clauses, params = [], []if "CreateTime" in columns:if startCreateTime > 0:where_clauses.append("CreateTime > ?")params.append(startCreateTime)if endCreateTime > 0:where_clauses.append("CreateTime < ?")params.append(endCreateTime)# 如果有WHERE子句,将其添加到SQL语句中,并添加ORDER BY子句sql = f"{sql_base} WHERE {' AND '.join(where_clauses)} ORDER BY CreateTime" if where_clauses else sql_basesrc_data = execute_sql(outdb, sql, tuple(params))if not src_data or len(src_data) < 1:continue# 插入数据sql = f"INSERT OR IGNORE INTO {table} ({','.join([i for i in columns])}) VALUES ({','.join(['?'] * len(columns))})"try:out_cursor.executemany(sql, src_data)# update sync_logsql_update_sync_log = ("UPDATE sync_log ""SET src_count = ? ,"f"current_count=(SELECT COUNT(*) FROM {table}) ""WHERE db_path=? AND tbl_name=?")out_cursor.execute(sql_update_sync_log, (src_count, db_path, table))except Exception as e:wx_core_loger.error(f"error: {db_path}\n{de_path}\n{table}\n{sql}\n{src_data}\n{len(src_data)}\n{e}\n",exc_info=True)# 分离数据库sql_detach = f"DETACH DATABASE {alias}"out_cursor.execute(sql_detach)outdb.commit()out_cursor.close()outdb.close()return save_path# @wx_core_error
# def merge_db1(db_paths: list[dict], save_path: str = "merge.db", is_merge_data: bool = True,
#               startCreateTime: int = 0, endCreateTime: int = 0):
#     """
#     合并数据库 会忽略主键以及重复的行。
#     :param db_paths: [{"db_path": "xxx", "de_path": "xxx"},...]
#                         db_path表示初始路径,de_path表示解密后的路径;初始路径用于保存合并的日志情况,解密后的路径用于读取数据
#     :param save_path: str 输出文件路径
#     :param is_merge_data: bool 是否合并数据(如果为False,则只解密,并创建表,不插入数据)
#     :param startCreateTime: 开始时间戳 主要用于MSG数据库的合并
#     :param endCreateTime:  结束时间戳 主要用于MSG数据库的合并
#     :return:
#     """
#     if os.path.isdir(save_path):
#         save_path = os.path.join(save_path, f"merge_{int(time.time())}.db")
#
#     if isinstance(db_paths, list):
#         # alias, file_path
#         databases = {f"MSG{i}": (db['db_path'],
#                                  db.get('de_path', db['db_path'])
#                                  ) for i, db in enumerate(db_paths)
#                      }
#     else:
#         raise TypeError("db_paths 类型错误")
#
#     from sqlalchemy import create_engine, MetaData, Table, select, insert, Column, UniqueConstraint
#     from sqlalchemy.orm import sessionmaker
#     from sqlalchemy import inspect, PrimaryKeyConstraint
#
#     outdb = create_engine(f"sqlite:///{save_path}", echo=False)
#
#     # 创建Session实例
#     Session = sessionmaker()
#     Session.configure(bind=outdb)
#     session = Session()
#
#     # 将MSG_db_paths中的数据合并到out_db_path中
#     for alias, db in databases.items():
#         db_path = db[0]
#         de_path = db[1]
#
#         db_engine = create_engine(f"sqlite:///{de_path}", echo=False)
#
#         # 反射源数据库的表结构
#         metadata = MetaData()
#         metadata.reflect(bind=db_engine)
#
#         # 创建表
#         outdb_metadata = MetaData()
#         inspector = inspect(db_engine)
#         table_names = [i for i in inspector.get_table_names() if i not in ["sqlite_sequence"]]
#         for table_name in table_names:
#             # 创建表table
#             columns_list_dict = inspector.get_columns(table_name)
#             col_names = [i['name'] for i in columns_list_dict]
#             columns = [Column(i['name'], i['type'], primary_key=False) for i in columns_list_dict]
#             table = Table(table_name, outdb_metadata, *columns)
#             if len(columns) > 1:  # 联合索引
#                 unique_constraint = UniqueConstraint(*col_names, name=f"{table_name}_unique_index")
#                 table.append_constraint(unique_constraint)
#             else:
#                 table.append_constraint(PrimaryKeyConstraint(*col_names))
#             table.create(outdb, checkfirst=True)
#
#         # 将源数据库中的数据插入目标数据库
#         outdb_metadata = MetaData()
#         for table_name in metadata.tables:
#             source_table = Table(table_name, metadata, autoload_with=db_engine)
#             outdb_table = Table(table_name, outdb_metadata, autoload_with=outdb)
#
#             # 查询源表中的所有数据
#             query = select(source_table)
#             with db_engine.connect() as connection:
#                 result = connection.execute(query).fetchall()
#
#             # 插入到目标表中
#             for row in result:
#                 row_data = row._asdict()
#
#                 # 尝试将所有文本数据转换为 UTF-8
#                 for key, value in row_data.items():
#                     if isinstance(value, str):
#                         row_data[key] = value.encode("utf-8")
#
#                 insert_stmt = insert(outdb_table).values(row_data)
#                 try:
#                     session.execute(insert_stmt)
#                 except Exception as e:
#                     pass
#         db_engine.dispose()
#
#     # 提交事务
#     session.commit()
#     # 关闭Session
#     session.close()
#     outdb.dispose()
#     return save_path@wx_core_error
def decrypt_merge(wx_path: str, key: str, outpath: str = "",merge_save_path: str = None,is_merge_data=True, is_del_decrypted: bool = True,startCreateTime: int = 0, endCreateTime: int = 0,db_type=None) -> (bool, str):"""解密合并数据库 msg.db, microMsg.db, media.db,注意:会删除原数据库:param wx_path: 微信路径 eg: C:\\*******\\WeChat Files\\wxid_*********:param key: 解密密钥:param outpath: 输出路径:param merge_save_path: 合并后的数据库路径:param is_merge_data: 是否合并数据(如果为False,则只解密,并创建表,不插入数据):param is_del_decrypted: 是否删除解密后的数据库(除了合并后的数据库):param startCreateTime: 开始时间戳 主要用于MSG数据库的合并:param endCreateTime:  结束时间戳 主要用于MSG数据库的合并:param db_type: 数据库类型,从核心数据库中选择:return: (true,解密后的数据库路径) or (false,错误信息)"""if db_type is None:db_type = []outpath = outpath if outpath else "decrypt_merge_tmp"merge_save_path = os.path.join(outpath,f"merge_{int(time.time())}.db") if merge_save_path is None else merge_save_pathdecrypted_path = os.path.join(outpath, "decrypted")if not wx_path or not key or not os.path.exists(wx_path):wx_core_loger.error("参数错误", exc_info=True)return False, "参数错误"# 解密code, wxdbpaths = get_core_db(wx_path, db_type)if not code:wx_core_loger.error(f"获取数据库路径失败{wxdbpaths}", exc_info=True)return False, wxdbpaths# 判断out_path是否为空目录if os.path.exists(decrypted_path) and os.listdir(decrypted_path):for root, dirs, files in os.walk(decrypted_path, topdown=False):for name in files:os.remove(os.path.join(root, name))for name in dirs:os.rmdir(os.path.join(root, name))if not os.path.exists(decrypted_path):os.makedirs(decrypted_path)wxdbpaths = {i["db_path"]: i for i in wxdbpaths}# 调用 decrypt 函数,并传入参数   # 解密code, ret = batch_decrypt(key=key, db_path=list(wxdbpaths.keys()), out_path=decrypted_path, is_print=False)if not code:wx_core_loger.error(f"解密失败{ret}", exc_info=True)return False, retout_dbs = []for code1, ret1 in ret:if code1:out_dbs.append(ret1)parpare_merge_db_path = []for db_path, out_path, _ in out_dbs:parpare_merge_db_path.append({"db_path": db_path, "de_path": out_path})merge_save_path = merge_db(parpare_merge_db_path, merge_save_path, is_merge_data=is_merge_data,startCreateTime=startCreateTime, endCreateTime=endCreateTime)if is_del_decrypted:shutil.rmtree(decrypted_path, True)if isinstance(merge_save_path, str):return True, merge_save_pathelse:return False, "未知错误"@wx_core_error
def merge_real_time_db(key, merge_path: str, db_paths: [dict] or dict, real_time_exe_path: str = None):"""合并实时数据库消息,暂时只支持64位系统:param key:  解密密钥:param merge_path:  合并后的数据库路径:param db_paths:  [dict] or dict eg: {'wxid': 'wxid_***', 'db_type': 'MicroMsg','db_path': 'C:\**\wxid_***\Msg\MicroMsg.db', 'wxid_dir': 'C:\***\wxid_***'}:param real_time_exe_path:  实时数据库合并工具路径:return:"""try:import platformexcept:raise ImportError("未找到模块 platform")# 判断系统位数是否为64位,如果不是则抛出异常if platform.architecture()[0] != '64bit':raise Exception("System is not 64-bit.")if isinstance(db_paths, dict):db_paths = [db_paths]merge_path = os.path.abspath(merge_path)  # 合并后的数据库路径,必须为绝对路径merge_path_base = os.path.dirname(merge_path)  # 合并后的数据库路径if not os.path.exists(merge_path_base):os.makedirs(merge_path_base)endbs = []for db_info in db_paths:db_path = os.path.abspath(db_info['db_path'])if not os.path.exists(db_path):# raise FileNotFoundError("数据库不存在")continueendbs.append(os.path.abspath(db_path))endbs = '" "'.join(list(set(endbs)))if not os.path.exists(real_time_exe_path if real_time_exe_path else ""):current_path = os.path.dirname(__file__)  # 获取当前文件夹路径real_time_exe_path = os.path.join(current_path, "tools", "realTime.exe")if not os.path.exists(real_time_exe_path):raise FileNotFoundError("未找到实时数据库合并工具")real_time_exe_path = os.path.abspath(real_time_exe_path)# 调用cmd命令cmd = f'{real_time_exe_path} "{key}" "{merge_path}" "{endbs}"'# os.system(cmd)# wx_core_loger.info(f"合并实时数据库命令:{cmd}")p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=merge_path_base,creationflags=subprocess.CREATE_NO_WINDOW)out, err = p.communicate()  # 查看返回值if out and out.decode("utf-8").find("SUCCESS") >= 0:wx_core_loger.info(f"合并实时数据库成功{out}")return True, merge_pathelse:wx_core_loger.error(f"合并实时数据库失败\n{out}\n{err}")return False, (out, err)@wx_core_error
def all_merge_real_time_db(key, wx_path, merge_path: str, real_time_exe_path: str = None):"""合并所有实时数据库注:这是全量合并,会有可能产生重复数据,需要自行去重:param key:  解密密钥:param wx_path:  微信文件夹路径 eg:C:\*****\WeChat Files\wxid*******:param merge_path:  合并后的数据库路径 eg: C:\\*******\\WeChat Files\\wxid_*********\\merge.db:param real_time_exe_path:  实时数据库合并工具路径:return:"""if not merge_path or not key or not wx_path or not wx_path:return False, "msg_path or media_path or wx_path or key is required"try:from wxdump import get_core_dbexcept ImportError:return False, "未找到模块 wxdump"db_paths = get_core_db(wx_path, CORE_DB_TYPE)if not db_paths[0]:return False, db_paths[1]db_paths = db_paths[1]code, ret = merge_real_time_db(key=key, merge_path=merge_path, db_paths=db_paths,real_time_exe_path=real_time_exe_path)if code:return True, merge_pathelse:return False, ret

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/8478.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

python数据结构基础(7)

本节学习最后一种数据结构---图,在很多问题中应用图可以帮助构建思维空间,快速理清思路,解决复杂问题. 图就是一些顶点的集合,这些顶点通过一系列边链接起来.根据边的有向和无向,图分为有向图和无向图.有时图的边上带有权重,本节暂时不将权重作为重点. 计算机通过邻接表或者邻…

NAS的软件和生态才是王道!谈谈群晖是如何凭实力“躺平”的

NAS的软件和生态才是王道&#xff01;谈谈群晖是如何凭实力“躺平”的 哈喽小伙伴们好&#xff0c;我是Stark-C~ 近几年随着大家对于数据存储和管理需求的增加&#xff0c;以及各大网盘变相收费和涨价&#xff0c;并且还不合时宜的爆出隐私泄露问题。而NAS&#xff08;网络附…

阅读笔记 Contemporary strategy analysis Chapter 14

来源&#xff1a;Robert M. Grant - Contemporary strategy analysis (2018) Chapter 14 External Growth Strategies: Mergers, Acquisitions, and Alliances 合并、收购和联盟 Ⅰ Introduction and Objectives 企业并购与联盟是公司实现快速扩张的重要战略工具。通过这些手段…

Java 8 Stream API 详解

在 Java 8 中&#xff0c;引入了一个全新的 API——Stream API&#xff0c;它与传统的 java.io 包下的 InputStream 和 OutputStream 没有任何关系。Stream API 的引入主要是为了提高程序员在操作集合&#xff08;Collection&#xff09;时的生产力&#xff0c;而这一提升很大程…

VMware替代实战手册:更高效的MySQL数据库迁移方案

数据库作为数字化用户的核心资产&#xff0c;其迁移是一项复杂且重要的任务&#xff0c;特别是在VMware平台替换及IT基础设施更新换代之时&#xff0c;尤其需要保障数据库迁移过程的平稳、流畅。 深信服推出的数据库管理平台&#xff08;DMP&#xff09;是为关系型数据库量身打…

GaussDB高智能--库内AI引擎:模型管理数据集管理

3.2 模型管理 在机器学习算法进行训练后&#xff0c;生成的模型需要进行存储&#xff0c;以便后续推理进行使用。训练过程的时序图如下&#xff1a; 在训练过程中&#xff0c;最后一步是通过调用store_model接口&#xff0c;在系统表gs_model_warehouse中插入一条记录&#…

使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径

在进行WPF开发时&#xff0c;System.Windows.Forms.FolderBrowserDialog的选择文件夹功能不支持输入路径&#xff1a; 希望能够获得下图所示的选择文件夹功能&#xff1a; 于是&#xff0c;通过NuGet中安装Ookii.Dialogs.Wpf包&#xff0c;并创建一个简单的工具类&#xff1a; …

求助帖【如何学习核磁共振的原理】

最近提前进组了 我完全不懂磁共振的相关知识 想问问各位大佬有没有推荐的学习路线 或者是学习资料、论坛都可以的&#xff08;我做的方向是磁共振成像技术&#xff09; 老师给了一本书&#xff0c;但是有点看不懂&#xff0c;全英文的 叫Principles Of Magnetic Resonance …

【自动化测试】如何在jenkins中搭建allure

相信大家在做自动化测试过程中&#xff0c;都会用到自动化测试环境&#xff0c;目前最常见的就是通过容器化方式部署自动化测试环境&#xff0c;但对于一些测试小白&#xff0c;不是很会搭建持续集成环境&#xff0c;特别是从0-1的过程&#xff0c;需要自行搭建很多依赖环境&am…

人才流失预测模型(机器学习)

1. 项目描述 ​ 企业的快速发展离不开人才的支撑&#xff0c;可是现在我国的企业的人才流失严重&#xff0c;人才流失问题现在已经成为了关系企业发展的一个重大的问题。这些企业要想在目前激烈的竞争中快速发展&#xff0c;就需要依靠自身的人力资源的来竞争。只有拥有比对方…

【Mac】安装 VMware Fusion Pro

VMware Fusion Pro 软件已经正式免费提供给个人用户使用&#xff01; 1、下载 【官网】 下拉找到 VMware Fusion Pro Download 登陆账号 如果没有账号&#xff0c;点击右上角 LOGIN &#xff0c;选择 REGISTER 注册信息除了邮箱外可随意填写 登陆时&#xff0c;Username为…

Java版企业电子招标采购系统源码Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

【el-pagination的使用及修改分页组件的整体大小修改默认样式的宽度详细教程】

今天遇到个bug&#xff0c;使用element-puls中的分页的时候&#xff0c;长度会超出盒子&#xff0c;今天教大家如何修改el-pagination的宽度&#xff0c;以及修改分页组件的整体大小 直接修改 style"width: 100%; margin-top: 10px"不生效 控制台修改el-pagination…

单体架构的 IM 系统设计

先直接抛出业务背景&#xff01; 有一款游戏&#xff0c;日活跃量&#xff08;DAU&#xff09;在两千左右&#xff0c;虽然 DAU 不高&#xff0c;但这两千用户的忠诚度非常高&#xff0c;而且会持续为游戏充值&#xff1b;为了进一步提高用户体验&#xff0c;继续增强用户的忠…

设计模式之单列模式(7种单例模式案例,Effective Java 作者推荐枚举单例模式)

前言 在设计模式中按照不同的处理方式共包含三大类&#xff1b;创建型模式、结构型模式和行为模式&#xff0c;其中创建型模式目前已经介绍了其中的四个&#xff1b;工厂方法模式、抽象工厂模式、生成器模式和原型模式&#xff0c;除此之外还有最后一个单例模式。 单列模式介绍…

具有扩展卷积的DCNN

目的&#xff1a; 进行了一系列实验来证明&#xff0c;在没有任何全连接层的情况下&#xff0c;具有扩展卷积的 DCNN 的性能并不比广泛使用的具有包含收缩&#xff08;无零填充&#xff09;卷积层和多个全连接层的混合结构的深度神经网络差。 DCNN&#xff1a;Deep convoluti…

【linux】查看不同网络命名空间的端口

在部署harbor时&#xff0c;内部用的是数据库postgresql&#xff0c;端口默认是: 5432&#xff0c;一开始以为这个数据库docker容器是在本命名空间中&#xff0c;一直用ss -lnt查询系统的端口&#xff0c;找不到5432端口。但是harbor要能正常使用&#xff0c;所有怀疑harbor的容…

W5500-EVB-Pico2评估板介绍

目录 1 概述 2 板载资源 2.1 硬件规格 2.2 硬件规格 2.3 工作条件 3 参考资料 3.1 RP2350 数据手册 3.2 W5500 数据手册 3.3 原理图 原理图 & 物料清单 & Gerber 文件 3.3 尺寸图 (单位 : mm) 3.4 参考例程 认证 CE FCC AWS 资质 Microsoft Azure 认证…

2024MoonBit全球编程创新挑战赛参赛作品“飞翔的小鸟”技术开发指南

本文转载自 CSDN&#xff1a;https://blog.csdn.net/m0_61243965/article/details/143510089作者&#xff1a;言程序plus 实战开发基于moonbit和wasm4的飞翔的小鸟游戏 游戏中&#xff0c;玩家需要通过上下左右按键控制Bird&#xff0c;在不断移动的障碍pipe之间穿梭&#xf…

认证授权基础概念详解

目录 认证 (Authentication) 和授权 (Authorization)的区别是什么&#xff1f; RBAC 模型了解吗&#xff1f; 什么是 Cookie ? Cookie 的作用是什么? 如何在项目中使用 Cookie 呢&#xff1f; 如何在 Spring Boot 中创建和读取 Cookie 创建 Cookie Cookie 到期日期 安全…