提交
This commit is contained in:
1 parent
d336fd0226
commit
2e9d020d50
3454 files changed
+878963
-319
No files matched your search
@@ -0,0 +1,17 @@
|
||||
-- /api/web/admin/api-request-logs 列表性能索引补强
|
||||
-- 典型查询:
|
||||
-- WHERE is_del=0 [AND http_method=?] [AND status_code=?]
|
||||
-- [AND create_at between ? and ?]
|
||||
-- ORDER BY create_at DESC
|
||||
|
||||
-- 默认列表 + 时间倒序分页
|
||||
ALTER TABLE sys_api_request_log
|
||||
ADD INDEX idx_arl_isdel_createat (is_del, create_at);
|
||||
|
||||
-- 按方法过滤
|
||||
ALTER TABLE sys_api_request_log
|
||||
ADD INDEX idx_arl_isdel_method_createat (is_del, http_method, create_at);
|
||||
|
||||
-- 按状态码过滤
|
||||
ALTER TABLE sys_api_request_log
|
||||
ADD INDEX idx_arl_isdel_status_createat (is_del, status_code, create_at);
|
||||
@@ -0,0 +1,12 @@
|
||||
-- 给 /api/web/admin/products 列表(GET,前端加密传输时为 POST + X-HTTP-Method-Override: GET)做索引补强
|
||||
-- 典型 SQL:WHERE is_del = 0 [AND category_id = ?] [AND (name LIKE ... OR description LIKE ...)] ORDER BY create_at DESC
|
||||
-- LIKE '%keyword%' 无法有效走 name/description 索引;无关键词时下面索引可帮助 COUNT + 分页排序。
|
||||
-- 执行前请先 SHOW INDEX FROM bas_product; 若索引已存在可跳过对应语句。
|
||||
|
||||
-- 1) 全量列表 / 仅启用筛选:is_del + 排序字段
|
||||
ALTER TABLE bas_product
|
||||
ADD INDEX idx_product_isdel_createat (is_del, create_at);
|
||||
|
||||
-- 2) 按分类筛选 + 排序(与 1 并存;分类列表走此索引更省扫描)
|
||||
ALTER TABLE bas_product
|
||||
ADD INDEX idx_product_isdel_category_createat (is_del, category_id, create_at);
|
||||
@@ -0,0 +1,26 @@
|
||||
-- /api/web/delivery-tasks 列表性能索引补强
|
||||
-- 目标查询特征:
|
||||
-- 1) WHERE is_del=0 ORDER BY create_at DESC(默认列表)
|
||||
-- 2) WHERE is_del=0 AND region_id=? AND status=? ORDER BY create_at DESC
|
||||
-- 3) WHERE is_del=0 AND deliver_id=? ORDER BY create_at DESC
|
||||
-- 4) 列表中按 task_id 聚合明细数量(biz_delivery_task_item)
|
||||
|
||||
-- 1) 默认列表与时间倒序分页
|
||||
ALTER TABLE biz_delivery_task
|
||||
ADD INDEX idx_dt_isdel_createat (is_del, create_at);
|
||||
|
||||
-- 2) 区域 + 状态过滤场景
|
||||
ALTER TABLE biz_delivery_task
|
||||
ADD INDEX idx_dt_isdel_region_status_createat (is_del, region_id, status, create_at);
|
||||
|
||||
-- 3) 配送员维度过滤场景
|
||||
ALTER TABLE biz_delivery_task
|
||||
ADD INDEX idx_dt_isdel_deliver_createat (is_del, deliver_id, create_at);
|
||||
|
||||
-- 4) 供应商维度过滤场景(供应商端任务列表)
|
||||
ALTER TABLE biz_delivery_task
|
||||
ADD INDEX idx_dt_isdel_supplier_createat (is_del, supplier_id, create_at);
|
||||
|
||||
-- 5) 列表 itemCount 聚合:WHERE task_id IN (...) AND is_del=0 GROUP BY task_id
|
||||
ALTER TABLE biz_delivery_task_item
|
||||
ADD INDEX idx_dti_task_isdel (task_id, is_del);
|
||||
@@ -0,0 +1,21 @@
|
||||
-- 给 /api/web/admin/orders 列表接口做性能索引补强(幂等尽量实现)
|
||||
-- 注意:不同 MySQL 版本对 IF NOT EXISTS 支持不同;若执行失败,请先 SHOW INDEX 再手动判断是否已存在。
|
||||
|
||||
-- 1) 覆盖最常见列表:WHERE is_del=0 ORDER BY create_at DESC
|
||||
-- 目标:减少 Using filesort / 全表扫描风险
|
||||
ALTER TABLE biz_order
|
||||
ADD INDEX idx_order_isdel_createat (is_del, create_at);
|
||||
|
||||
-- 2) 常用筛选:区域 + 订单主状态 + 时间排序
|
||||
ALTER TABLE biz_order
|
||||
ADD INDEX idx_order_isdel_region_orderstatus_createat (is_del, region_id, order_status, create_at);
|
||||
|
||||
-- 3) 常用筛选:区域 + 创建时间范围(startDate/endDate)
|
||||
ALTER TABLE biz_order
|
||||
ADD INDEX idx_order_isdel_region_createat (is_del, region_id, create_at);
|
||||
|
||||
-- 4) 单号查询建议:
|
||||
-- 当前实现是 order_no LIKE '%xxx%',该写法通常无法走索引。
|
||||
-- 若业务可改为前缀匹配(LIKE 'xxx%')或精确匹配(=),将显著提升性能。
|
||||
-- 订单号已有唯一索引 uk_order_no(可用于 '=' 或前缀匹配)。
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
-- /api/web/admin/price-versions 列表性能索引补强
|
||||
-- 典型查询:
|
||||
-- WHERE is_del=0 [AND region_id=?] [AND supplier_id=?] [AND status=?]
|
||||
-- ORDER BY create_at DESC
|
||||
|
||||
-- 默认列表 + 时间倒序分页
|
||||
ALTER TABLE biz_price_list
|
||||
ADD INDEX idx_pl_isdel_createat (is_del, create_at);
|
||||
|
||||
-- 区域维度过滤
|
||||
ALTER TABLE biz_price_list
|
||||
ADD INDEX idx_pl_isdel_region_createat (is_del, region_id, create_at);
|
||||
|
||||
-- 供应商维度过滤
|
||||
ALTER TABLE biz_price_list
|
||||
ADD INDEX idx_pl_isdel_supplier_createat (is_del, supplier_id, create_at);
|
||||
|
||||
-- 状态维度过滤
|
||||
ALTER TABLE biz_price_list
|
||||
ADD INDEX idx_pl_isdel_status_createat (is_del, status, create_at);
|
||||
@@ -0,0 +1,7 @@
|
||||
-- 询价报价界面“区域”下拉/Tab:启用区域列表索引补强
|
||||
-- 对应接口:GET /api/web/admin/regions/active
|
||||
-- 查询:WHERE is_del=0 AND is_active=1 ORDER BY sort_order
|
||||
|
||||
ALTER TABLE sys_region
|
||||
ADD INDEX idx_region_isdel_active_sort (is_del, is_active, sort_order);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python3
|
||||
"""清空订单及相关表(开发调试用)。用法:在 backend 目录执行 python scripts/clear_biz_orders.py"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
from app.core.database import engine
|
||||
|
||||
TABLES = [
|
||||
"biz_order_status_log_his",
|
||||
"biz_order_status_log",
|
||||
"biz_order_item",
|
||||
"biz_order_his",
|
||||
"biz_order_adjust_his",
|
||||
"biz_order_adjust",
|
||||
"biz_wechat_pay",
|
||||
"biz_refund_his",
|
||||
"biz_refund",
|
||||
"biz_settlement_his",
|
||||
"biz_settlement",
|
||||
"biz_invoice",
|
||||
"biz_deliverer_review_his",
|
||||
"biz_deliverer_review",
|
||||
"biz_delivery_warning_his",
|
||||
"biz_delivery_warning",
|
||||
"biz_delivery_task_item",
|
||||
"biz_delivery_task_his",
|
||||
"biz_delivery_task",
|
||||
"biz_order",
|
||||
]
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with engine.begin() as conn:
|
||||
conn.execute(text("SET FOREIGN_KEY_CHECKS=0"))
|
||||
for t in TABLES:
|
||||
conn.execute(text(f"TRUNCATE TABLE `{t}`"))
|
||||
conn.execute(text("SET FOREIGN_KEY_CHECKS=1"))
|
||||
print("已清空订单及相关表:", ", ".join(TABLES))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env python3
|
||||
"""批量替换数据库历史 URL 前缀(MinIO 域名迁移脚本)。
|
||||
|
||||
用法(在 backend 目录执行):
|
||||
1) 先预览:
|
||||
python scripts/migrate_minio_urls.py --dry-run
|
||||
|
||||
2) 正式执行:
|
||||
python scripts/migrate_minio_urls.py
|
||||
|
||||
3) 自定义前缀:
|
||||
python scripts/migrate_minio_urls.py \
|
||||
--old-prefix "https://www.chengyantek.cn:20004" \
|
||||
--new-prefix "https://www.chengyantek.cn"
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy import text
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from app.core.database import engine # noqa: E402
|
||||
|
||||
|
||||
DEFAULT_OLD_PREFIX = "https://www.chengyantek.cn:20004"
|
||||
DEFAULT_NEW_PREFIX = "https://www.chengyantek.cn"
|
||||
|
||||
TEXT_TYPES = (
|
||||
"char",
|
||||
"varchar",
|
||||
"tinytext",
|
||||
"text",
|
||||
"mediumtext",
|
||||
"longtext",
|
||||
)
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="批量替换数据库中的 URL 前缀")
|
||||
parser.add_argument("--old-prefix", default=DEFAULT_OLD_PREFIX, help="旧 URL 前缀")
|
||||
parser.add_argument("--new-prefix", default=DEFAULT_NEW_PREFIX, help="新 URL 前缀")
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="仅预览受影响记录数,不执行更新",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def quote_ident(name: str) -> str:
|
||||
"""安全引用 MySQL 标识符。"""
|
||||
return f"`{name.replace('`', '``')}`"
|
||||
|
||||
|
||||
def get_text_columns(conn) -> list[tuple[str, str]]:
|
||||
sql = text(
|
||||
"""
|
||||
SELECT table_name, column_name
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND data_type IN :types
|
||||
ORDER BY table_name, ordinal_position
|
||||
"""
|
||||
)
|
||||
rows = conn.execute(sql, {"types": TEXT_TYPES}).fetchall()
|
||||
return [(r[0], r[1]) for r in rows]
|
||||
|
||||
|
||||
def count_affected(conn, table_name: str, column_name: str, old_prefix: str) -> int:
|
||||
sql = text(
|
||||
f"""
|
||||
SELECT COUNT(*) AS cnt
|
||||
FROM {quote_ident(table_name)}
|
||||
WHERE {quote_ident(column_name)} LIKE :pattern
|
||||
"""
|
||||
)
|
||||
return int(conn.execute(sql, {"pattern": f"%{old_prefix}%"}).scalar() or 0)
|
||||
|
||||
|
||||
def update_column(
|
||||
conn,
|
||||
table_name: str,
|
||||
column_name: str,
|
||||
old_prefix: str,
|
||||
new_prefix: str,
|
||||
) -> int:
|
||||
sql = text(
|
||||
f"""
|
||||
UPDATE {quote_ident(table_name)}
|
||||
SET {quote_ident(column_name)} = REPLACE({quote_ident(column_name)}, :old_prefix, :new_prefix)
|
||||
WHERE {quote_ident(column_name)} LIKE :pattern
|
||||
"""
|
||||
)
|
||||
result = conn.execute(
|
||||
sql,
|
||||
{
|
||||
"old_prefix": old_prefix,
|
||||
"new_prefix": new_prefix,
|
||||
"pattern": f"%{old_prefix}%",
|
||||
},
|
||||
)
|
||||
return int(result.rowcount or 0)
|
||||
|
||||
|
||||
def make_backup_table_name(table_name: str, suffix: str) -> str:
|
||||
"""生成备份表名,控制在 MySQL 64 字符限制内。"""
|
||||
max_table_len = 64
|
||||
reserve = len("_bak_") + len(suffix)
|
||||
max_base_len = max_table_len - reserve
|
||||
base = table_name[:max_base_len]
|
||||
return f"{base}_bak_{suffix}"
|
||||
|
||||
|
||||
def backup_table(conn, table_name: str, backup_table_name: str) -> None:
|
||||
"""备份单张表(结构+数据)。"""
|
||||
conn.execute(
|
||||
text(
|
||||
f"CREATE TABLE {quote_ident(backup_table_name)} LIKE {quote_ident(table_name)}"
|
||||
)
|
||||
)
|
||||
conn.execute(
|
||||
text(
|
||||
f"INSERT INTO {quote_ident(backup_table_name)} SELECT * FROM {quote_ident(table_name)}"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
old_prefix = args.old_prefix.strip().rstrip("/")
|
||||
new_prefix = args.new_prefix.strip().rstrip("/")
|
||||
dry_run = args.dry_run
|
||||
|
||||
if not old_prefix or not new_prefix:
|
||||
raise ValueError("old-prefix/new-prefix 不能为空")
|
||||
if old_prefix == new_prefix:
|
||||
raise ValueError("old-prefix 与 new-prefix 不能相同")
|
||||
|
||||
print(f"数据库 URL 迁移开始,模式:{'预览(dry-run)' if dry_run else '正式执行'}")
|
||||
print(f"旧前缀: {old_prefix}")
|
||||
print(f"新前缀: {new_prefix}")
|
||||
|
||||
total_rows = 0
|
||||
hit_columns = 0
|
||||
affected_items: list[tuple[str, str, int]] = []
|
||||
|
||||
with engine.begin() as conn:
|
||||
columns = get_text_columns(conn)
|
||||
for table_name, column_name in columns:
|
||||
affected = count_affected(conn, table_name, column_name, old_prefix)
|
||||
if affected <= 0:
|
||||
continue
|
||||
affected_items.append((table_name, column_name, affected))
|
||||
|
||||
if not affected_items:
|
||||
print("未发现需要替换的数据,已结束。")
|
||||
return
|
||||
|
||||
if not dry_run:
|
||||
backup_suffix = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
backup_tables: dict[str, str] = {}
|
||||
for table_name, _, _ in affected_items:
|
||||
if table_name in backup_tables:
|
||||
continue
|
||||
backup_name = make_backup_table_name(table_name, backup_suffix)
|
||||
backup_table(conn, table_name, backup_name)
|
||||
backup_tables[table_name] = backup_name
|
||||
print(f"[BACKUP] {table_name} -> {backup_name}")
|
||||
|
||||
print(f"备份完成:共备份 {len(backup_tables)} 张表。")
|
||||
|
||||
for table_name, column_name, affected in affected_items:
|
||||
hit_columns += 1
|
||||
if dry_run:
|
||||
print(f"[DRY-RUN] {table_name}.{column_name} -> {affected} 行")
|
||||
total_rows += affected
|
||||
continue
|
||||
|
||||
updated = update_column(conn, table_name, column_name, old_prefix, new_prefix)
|
||||
print(f"[UPDATED] {table_name}.{column_name} -> {updated} 行")
|
||||
total_rows += updated
|
||||
|
||||
if dry_run:
|
||||
print(f"预览完成:共 {hit_columns} 个字段命中,预计影响 {total_rows} 行。")
|
||||
else:
|
||||
print(f"执行完成:共更新 {hit_columns} 个字段,影响 {total_rows} 行。")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,68 @@
|
||||
-- 运营端权限改造:super_admin + 多区域 + 菜单 code 授权
|
||||
|
||||
-- 1) 角色迁移:admin -> super_admin
|
||||
UPDATE sys_user
|
||||
SET role = 'super_admin',
|
||||
update_at = NOW()
|
||||
WHERE is_del = 0
|
||||
AND role = 'admin';
|
||||
|
||||
-- 2) 用户-区域权限表
|
||||
CREATE TABLE IF NOT EXISTS sys_user_region_scope (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID',
|
||||
user_id VARCHAR(32) NOT NULL COMMENT '用户ID',
|
||||
region_id VARCHAR(32) NOT NULL COMMENT '区域ID',
|
||||
is_del TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
create_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
create_by VARCHAR(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
create_name VARCHAR(100) DEFAULT NULL COMMENT '创建人姓名',
|
||||
update_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
update_by VARCHAR(32) DEFAULT NULL COMMENT '修改人ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_user_region_scope (user_id, region_id, is_del),
|
||||
KEY idx_user_region_scope_user (user_id, is_del),
|
||||
KEY idx_user_region_scope_region (region_id, is_del)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户区域权限';
|
||||
|
||||
-- 3) 用户-区域权限历史表(无索引、无 is_del)
|
||||
CREATE TABLE IF NOT EXISTS sys_user_region_scope_his (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID',
|
||||
user_id VARCHAR(32) NOT NULL COMMENT '用户ID',
|
||||
region_id VARCHAR(32) NOT NULL COMMENT '区域ID',
|
||||
create_at DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
create_by VARCHAR(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
create_name VARCHAR(100) DEFAULT NULL COMMENT '创建人姓名',
|
||||
update_at DATETIME DEFAULT NULL COMMENT '修改时间',
|
||||
update_by VARCHAR(32) DEFAULT NULL COMMENT '修改人ID',
|
||||
his_create_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '历史记录创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户区域权限历史';
|
||||
|
||||
-- 4) 用户-菜单 code 权限表
|
||||
CREATE TABLE IF NOT EXISTS sys_user_menu_scope (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID',
|
||||
user_id VARCHAR(32) NOT NULL COMMENT '用户ID',
|
||||
menu_code VARCHAR(100) NOT NULL COMMENT '菜单编码',
|
||||
is_del TINYINT NOT NULL DEFAULT 0 COMMENT '是否删除',
|
||||
create_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
create_by VARCHAR(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
create_name VARCHAR(100) DEFAULT NULL COMMENT '创建人姓名',
|
||||
update_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
|
||||
update_by VARCHAR(32) DEFAULT NULL COMMENT '修改人ID',
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY uk_user_menu_scope (user_id, menu_code, is_del),
|
||||
KEY idx_user_menu_scope_user (user_id, is_del),
|
||||
KEY idx_user_menu_scope_code (menu_code, is_del)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户菜单权限';
|
||||
|
||||
-- 5) 用户-菜单 code 权限历史表(无索引、无 is_del)
|
||||
CREATE TABLE IF NOT EXISTS sys_user_menu_scope_his (
|
||||
id VARCHAR(32) NOT NULL COMMENT '主键ID',
|
||||
user_id VARCHAR(32) NOT NULL COMMENT '用户ID',
|
||||
menu_code VARCHAR(100) NOT NULL COMMENT '菜单编码',
|
||||
create_at DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||
create_by VARCHAR(32) DEFAULT NULL COMMENT '创建人ID',
|
||||
create_name VARCHAR(100) DEFAULT NULL COMMENT '创建人姓名',
|
||||
update_at DATETIME DEFAULT NULL COMMENT '修改时间',
|
||||
update_by VARCHAR(32) DEFAULT NULL COMMENT '修改人ID',
|
||||
his_create_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '历史记录创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户菜单权限历史';
|
||||
Reference in new issue
Block a user