补充修改
This commit is contained in:
@@ -3025,7 +3025,7 @@ class GetGuanliDetailView(APIView):
|
||||
|
||||
# 获取多次分红配置(按次数和会员组织)
|
||||
# 查询该管事的所有多次分红记录
|
||||
duoci_records = DuociFenhong.query.filter(UserUID=yonghuid).order_by('cishu')
|
||||
duoci_records = DuociFenhong.query.filter(yonghuid=yonghuid).order_by('cishu')
|
||||
custom_first = {} # 首次分红定制(cishu=1)
|
||||
extra_map = {} # 其他次数 { cishu: { huiyuan_id: { guanshi_fenhong, jieshao, jiage } } }
|
||||
|
||||
@@ -3206,7 +3206,7 @@ class UpdateGuanliView(APIView):
|
||||
# 4.2 首次定制分红(cishu=1)
|
||||
if custom_first is not None and isinstance(custom_first, dict):
|
||||
# 获取当前所有首次定制记录
|
||||
existing_first = DuociFenhong.query.filter(UserUID=yonghuid, cishu=1)
|
||||
existing_first = DuociFenhong.query.filter(yonghuid=yonghuid, cishu=1)
|
||||
# 前端传来的定制字典 {会员ID: 金额}
|
||||
for huiyuan_id, amount in custom_first.items():
|
||||
try:
|
||||
@@ -3215,7 +3215,7 @@ class UpdateGuanliView(APIView):
|
||||
continue
|
||||
if amount <= 0:
|
||||
# 金额为0或负数表示删除定制
|
||||
DuociFenhong.query.filter(UserUID=yonghuid, huiyuan=huiyuan_id, cishu=1).delete()
|
||||
DuociFenhong.query.filter(yonghuid=yonghuid, huiyuan=huiyuan_id, cishu=1).delete()
|
||||
else:
|
||||
DuociFenhong.query.update_or_create(
|
||||
yonghuid=yonghuid,
|
||||
@@ -3266,7 +3266,7 @@ class UpdateGuanliView(APIView):
|
||||
}
|
||||
)
|
||||
# 删除不在 keep_set 中的额外次数记录
|
||||
existing_extra = DuociFenhong.query.filter(UserUID=yonghuid, cishu__gte=2)
|
||||
existing_extra = DuociFenhong.query.filter(yonghuid=yonghuid, cishu__gte=2)
|
||||
for record in existing_extra:
|
||||
if (record.cishu, record.huiyuan) not in keep_set:
|
||||
record.delete()
|
||||
@@ -3716,7 +3716,7 @@ class GetZuzhangDetailView(APIView):
|
||||
m['zuzhangfc'] = str(m['zuzhangfc'])
|
||||
|
||||
# 获取多次分红配置(按次数和会员组织)
|
||||
duoci_records = DuociFenhong.query.filter(UserUID=yonghuid).order_by('cishu')
|
||||
duoci_records = DuociFenhong.query.filter(yonghuid=yonghuid).order_by('cishu')
|
||||
custom_first = {} # 首次分红定制(cishu=1)
|
||||
extra_map = {} # 其他次数 { cishu: { huiyuan_id: { zuzhang_fenhong, jieshao, jiage } } }
|
||||
|
||||
@@ -3889,7 +3889,7 @@ class UpdateZuzhangView(APIView):
|
||||
|
||||
# 4.2 首次定制分红(cishu=1)
|
||||
if custom_first is not None and isinstance(custom_first, dict):
|
||||
existing_first = DuociFenhong.query.filter(UserUID=yonghuid, cishu=1)
|
||||
existing_first = DuociFenhong.query.filter(yonghuid=yonghuid, cishu=1)
|
||||
for huiyuan_id, amount in custom_first.items():
|
||||
try:
|
||||
amount = Decimal(str(amount))
|
||||
@@ -3897,7 +3897,7 @@ class UpdateZuzhangView(APIView):
|
||||
continue
|
||||
if amount <= 0:
|
||||
# 删除定制(恢复默认)
|
||||
DuociFenhong.query.filter(UserUID=yonghuid, huiyuan=huiyuan_id, cishu=1).delete()
|
||||
DuociFenhong.query.filter(yonghuid=yonghuid, huiyuan=huiyuan_id, cishu=1).delete()
|
||||
else:
|
||||
DuociFenhong.query.update_or_create(
|
||||
yonghuid=yonghuid,
|
||||
@@ -3946,7 +3946,7 @@ class UpdateZuzhangView(APIView):
|
||||
}
|
||||
)
|
||||
# 删除不在 keep_set 中的额外次数记录
|
||||
existing_extra = DuociFenhong.query.filter(UserUID=yonghuid, cishu__gte=2)
|
||||
existing_extra = DuociFenhong.query.filter(yonghuid=yonghuid, cishu__gte=2)
|
||||
for record in existing_extra:
|
||||
if (record.cishu, record.huiyuan) not in keep_set:
|
||||
record.delete()
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""检查 user_dashou 中有 user_id 的记录"""
|
||||
import re
|
||||
|
||||
with open('xaio_cheng_xu_backup.sql', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
pattern = re.compile(r"INSERT\s+INTO\s+`user_dashou`\s+VALUES\s*\((.+?)\)\s*;", re.IGNORECASE | re.DOTALL)
|
||||
matches = list(pattern.finditer(content))
|
||||
|
||||
# 找有 user_id 的记录
|
||||
has_uid = 0
|
||||
no_uid = 0
|
||||
uid_values = []
|
||||
for m in matches:
|
||||
v = m.group(1).rstrip()
|
||||
if v.endswith('NULL'):
|
||||
no_uid += 1
|
||||
else:
|
||||
has_uid += 1
|
||||
# 提取最后一个值(user_id)
|
||||
last_comma = v.rfind(',')
|
||||
uid_val = v[last_comma+1:].strip()
|
||||
uid_values.append(uid_val)
|
||||
|
||||
print(f"有 user_id: {has_uid}, 无 user_id: {no_uid}")
|
||||
print(f"user_id 值样本: {uid_values[:10]}")
|
||||
|
||||
# 检查 user_main 中这些 user_id 是否存在
|
||||
# user_main 的 id 字段是 vals[1]
|
||||
main_pattern = re.compile(r"INSERT\s+INTO\s+`user_main`\s+VALUES\s*\((.+?)\)\s*;", re.IGNORECASE | re.DOTALL)
|
||||
main_ids = set()
|
||||
for m in main_pattern.finditer(content):
|
||||
# 简单提取第二个字段 (id)
|
||||
v = m.group(1)
|
||||
# 找前几个逗号
|
||||
in_q = False
|
||||
commas = []
|
||||
for i, ch in enumerate(v):
|
||||
if ch == "'": in_q = not in_q
|
||||
elif ch == ',' and not in_q:
|
||||
commas.append(i)
|
||||
if len(commas) >= 2: break
|
||||
if len(commas) >= 2:
|
||||
id_val = v[commas[0]+1:commas[1]].strip()
|
||||
main_ids.add(id_val)
|
||||
|
||||
print(f"\nuser_main id 范围: {len(main_ids)} 个")
|
||||
matched = sum(1 for uid in uid_values if uid in main_ids)
|
||||
print(f"user_dashou user_id 匹配 user_main: {matched}/{len(uid_values)}")
|
||||
|
||||
# 也检查 user_type 分布
|
||||
type_pattern = re.compile(r"INSERT\s+INTO\s+`user_main`\s+VALUES\s*\((.+?)\)\s*;", re.IGNORECASE | re.DOTALL)
|
||||
type_counts = {}
|
||||
for m in type_pattern.finditer(content):
|
||||
v = m.group(1)
|
||||
# 找第12个字段 (user_type, index 11)
|
||||
in_q = False
|
||||
commas = []
|
||||
for i, ch in enumerate(v):
|
||||
if ch == "'": in_q = not in_q
|
||||
elif ch == ',' and not in_q:
|
||||
commas.append(i)
|
||||
if len(commas) >= 12: break
|
||||
if len(commas) >= 12:
|
||||
type_val = v[commas[10]+1:commas[11]].strip().strip("'")
|
||||
type_counts[type_val] = type_counts.get(type_val, 0) + 1
|
||||
|
||||
print(f"\nuser_type 分布: {type_counts}")
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""检查数据库表行数"""
|
||||
import MySQLdb
|
||||
from app_secrets import DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME
|
||||
|
||||
conn = MySQLdb.connect(
|
||||
host=DATABASE_HOST, port=int(DATABASE_PORT),
|
||||
user=DATABASE_USER, password=DATABASE_PASSWORD,
|
||||
database=DATABASE_NAME
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"SELECT TABLE_NAME, TABLE_ROWS FROM information_schema.TABLES "
|
||||
"WHERE TABLE_SCHEMA = %s ORDER BY TABLE_NAME",
|
||||
[DATABASE_NAME]
|
||||
)
|
||||
for row in cursor.fetchall():
|
||||
print(f"{row[0]:40s} {row[1]}")
|
||||
conn.close()
|
||||
@@ -1,31 +0,0 @@
|
||||
import MySQLdb
|
||||
|
||||
conn = MySQLdb.connect(
|
||||
host='gvsds.com', port=50030, user='root',
|
||||
password='sajksh.sdfGH3YUge.wjkd+',
|
||||
database='xaio_cheng_xu', charset='utf8mb4'
|
||||
)
|
||||
cur = conn.cursor()
|
||||
|
||||
# 获取所有表
|
||||
cur.execute("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA='xaio_cheng_xu'")
|
||||
tables = [row[0] for row in cur.fetchall()]
|
||||
print(f'共 {len(tables)} 张表')
|
||||
|
||||
# 禁用外键检查,删除所有表
|
||||
cur.execute('SET FOREIGN_KEY_CHECKS = 0')
|
||||
for t in tables:
|
||||
cur.execute(f'DROP TABLE IF EXISTS `{t}`')
|
||||
print(f' 已删除: {t}')
|
||||
cur.execute('SET FOREIGN_KEY_CHECKS = 1')
|
||||
|
||||
conn.commit()
|
||||
print(f'\n已删除全部 {len(tables)} 张表')
|
||||
|
||||
# 验证
|
||||
cur.execute("SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='xaio_cheng_xu'")
|
||||
count = cur.fetchone()[0]
|
||||
print(f'剩余表数: {count}')
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
163
find.py
163
find.py
@@ -1,163 +0,0 @@
|
||||
"""
|
||||
REPL 模式文件内容搜索工具
|
||||
支持正则或纯文本搜索,默认搜索当前目录
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# 硬编码排除目录
|
||||
EXCLUDE_DIRS = {
|
||||
'.git', '.vs', 'node_modules', '__pycache__', '.idea',
|
||||
'miniprogram_npm', '_backup_pre_redesign',
|
||||
}
|
||||
|
||||
# 排除的文件扩展名
|
||||
EXCLUDE_EXTS = {
|
||||
'.png', '.jpg', '.jpeg', '.gif', '.svg', '.ico', '.bmp',
|
||||
'.mp3', '.mp4', '.wav', '.avi',
|
||||
'.zip', '.rar', '.7z', '.tar', '.gz',
|
||||
'.exe', '.dll', '.so', '.dylib',
|
||||
'.pyc', '.pyo', '.class', '.o', '.obj',
|
||||
'.db', '.sqlite', '.vsidx',
|
||||
}
|
||||
|
||||
# 默认搜索根目录
|
||||
DEFAULT_ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def should_skip(dirpath):
|
||||
"""判断目录是否应跳过"""
|
||||
parts = dirpath.replace('\\', '/').split('/')
|
||||
return any(p in EXCLUDE_DIRS for p in parts)
|
||||
|
||||
|
||||
def search(pattern, root=DEFAULT_ROOT, use_regex=True, max_results=50):
|
||||
"""搜索文件内容"""
|
||||
try:
|
||||
if use_regex:
|
||||
compiled = re.compile(pattern, re.IGNORECASE)
|
||||
except re.error as e:
|
||||
print(f' 正则语法错误: {e}')
|
||||
return
|
||||
|
||||
count = 0
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
# 就地修改 dirnames 以跳过排除目录
|
||||
dirnames[:] = [d for d in dirnames if d not in EXCLUDE_DIRS]
|
||||
|
||||
for fname in filenames:
|
||||
ext = os.path.splitext(fname)[1].lower()
|
||||
if ext in EXCLUDE_EXTS:
|
||||
continue
|
||||
|
||||
fpath = os.path.join(dirpath, fname)
|
||||
relpath = os.path.relpath(fpath, root)
|
||||
try:
|
||||
with open(fpath, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
for lineno, line in enumerate(f, 1):
|
||||
line_stripped = line.rstrip('\n\r')
|
||||
if use_regex:
|
||||
match = compiled.search(line_stripped)
|
||||
if match:
|
||||
highlight = line_stripped[:match.start()] + \
|
||||
f'>>> {match.group()} <<<' + \
|
||||
line_stripped[match.end():]
|
||||
print(f' {relpath}:{lineno} {highlight}')
|
||||
count += 1
|
||||
else:
|
||||
if pattern.lower() in line_stripped.lower():
|
||||
print(f' {relpath}:{lineno} {line_stripped}')
|
||||
count += 1
|
||||
|
||||
if count >= max_results:
|
||||
print(f'\n ... 已达上限 {max_results} 条,停止')
|
||||
return
|
||||
except (PermissionError, OSError):
|
||||
continue
|
||||
|
||||
if count == 0:
|
||||
print(' 未找到匹配内容')
|
||||
else:
|
||||
print(f'\n 共 {count} 条匹配')
|
||||
|
||||
|
||||
def main():
|
||||
print('=== 文件内容搜索工具 ===')
|
||||
print(f'搜索根目录: {DEFAULT_ROOT}')
|
||||
print(f'排除目录: {EXCLUDE_DIRS}')
|
||||
print()
|
||||
print('命令:')
|
||||
print(' /regex <pattern> 正则搜索')
|
||||
print(' /text <text> 纯文本搜索')
|
||||
print(' /max <n> 设置结果上限(默认50)')
|
||||
print(' /dir <path> 切换搜索目录')
|
||||
print(' /help 显示帮助')
|
||||
print(' /quit 退出')
|
||||
print()
|
||||
print('直接输入内容默认使用正则搜索')
|
||||
print()
|
||||
|
||||
root = DEFAULT_ROOT
|
||||
use_regex = True
|
||||
max_results = 50
|
||||
|
||||
while True:
|
||||
try:
|
||||
raw = input('find> ').strip()
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print('\n再见')
|
||||
break
|
||||
|
||||
if not raw:
|
||||
continue
|
||||
|
||||
if raw == '/quit':
|
||||
print('再见')
|
||||
break
|
||||
|
||||
if raw == '/help':
|
||||
print(' /regex <pattern> 正则搜索')
|
||||
print(' /text <text> 纯文本搜索')
|
||||
print(' /max <n> 设置结果上限')
|
||||
print(' /dir <path> 切换搜索目录')
|
||||
print(' /help 显示帮助')
|
||||
print(' /quit 退出')
|
||||
print(' 直接输入内容默认使用正则搜索')
|
||||
continue
|
||||
|
||||
if raw.startswith('/regex '):
|
||||
pattern = raw[7:].strip()
|
||||
if pattern:
|
||||
search(pattern, root, use_regex=True, max_results=max_results)
|
||||
continue
|
||||
|
||||
if raw.startswith('/text '):
|
||||
pattern = raw[6:].strip()
|
||||
if pattern:
|
||||
search(pattern, root, use_regex=False, max_results=max_results)
|
||||
continue
|
||||
|
||||
if raw.startswith('/max '):
|
||||
try:
|
||||
max_results = int(raw[5:].strip())
|
||||
print(f' 结果上限设为 {max_results}')
|
||||
except ValueError:
|
||||
print(' 请输入数字')
|
||||
continue
|
||||
|
||||
if raw.startswith('/dir '):
|
||||
new_dir = raw[5:].strip()
|
||||
if os.path.isdir(new_dir):
|
||||
root = os.path.abspath(new_dir)
|
||||
print(f' 搜索目录切换为: {root}')
|
||||
else:
|
||||
print(f' 目录不存在: {new_dir}')
|
||||
continue
|
||||
|
||||
# 默认正则搜索
|
||||
search(raw, root, use_regex=use_regex, max_results=max_results)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
67
fix_fk.py
67
fix_fk.py
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""修复 yonghu_dianpu_bangding 表的 FK 约束"""
|
||||
import MySQLdb
|
||||
from app_secrets import DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME
|
||||
|
||||
conn = MySQLdb.connect(
|
||||
host=DATABASE_HOST, port=int(DATABASE_PORT),
|
||||
user=DATABASE_USER, password=DATABASE_PASSWORD,
|
||||
database=DATABASE_NAME, charset='utf8mb4'
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 1. 查看所有引用 User 表的 FK 约束
|
||||
cursor.execute("""
|
||||
SELECT CONSTRAINT_NAME, TABLE_NAME, COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME
|
||||
FROM information_schema.KEY_COLUMN_USAGE
|
||||
WHERE TABLE_SCHEMA = %s AND REFERENCED_TABLE_NAME = 'User'
|
||||
""", [DATABASE_NAME])
|
||||
|
||||
fks = cursor.fetchall()
|
||||
print("引用 User 表的 FK 约束:")
|
||||
for fk in fks:
|
||||
print(f" {fk[0]}: {fk[1]}.{fk[2]} -> {fk[3]}.{fk[4]}")
|
||||
|
||||
# 2. 删除所有引用 User.UserUID 的 FK 约束(类型不兼容的)
|
||||
for fk in fks:
|
||||
if fk[4] == 'UserUID': # 引用了 UserUID (varchar) 而不是 UserUUID (binary)
|
||||
constraint_name = fk[0]
|
||||
table_name = fk[1]
|
||||
print(f"\n删除不兼容 FK: {constraint_name} on {table_name}")
|
||||
try:
|
||||
cursor.execute(f"ALTER TABLE `{table_name}` DROP FOREIGN KEY `{constraint_name}`")
|
||||
conn.commit()
|
||||
print(f" 已删除 {constraint_name}")
|
||||
except Exception as e:
|
||||
print(f" 删除失败: {e}")
|
||||
|
||||
# 3. 修改 yonghu_dianpu_bangding.UserUUID 列类型
|
||||
print("\n修改 yonghu_dianpu_bangding.UserUUID 列类型...")
|
||||
try:
|
||||
cursor.execute("ALTER TABLE yonghu_dianpu_bangding MODIFY COLUMN UserUUID binary(16) NOT NULL")
|
||||
conn.commit()
|
||||
print(" 修改成功")
|
||||
except Exception as e:
|
||||
print(f" 修改失败: {e}")
|
||||
|
||||
# 4. 重新添加 FK 约束
|
||||
print("\n重新添加 FK 约束...")
|
||||
try:
|
||||
cursor.execute("""
|
||||
ALTER TABLE yonghu_dianpu_bangding
|
||||
ADD CONSTRAINT yonghu_dianpu_bangding_UserUUID_fk
|
||||
FOREIGN KEY (UserUUID) REFERENCES User(UserUUID)
|
||||
""")
|
||||
conn.commit()
|
||||
print(" FK 约束添加成功")
|
||||
except Exception as e:
|
||||
print(f" FK 约束添加失败: {e}")
|
||||
|
||||
# 5. 验证
|
||||
print("\n验证表结构:")
|
||||
cursor.execute("DESCRIBE yonghu_dianpu_bangding")
|
||||
for row in cursor.fetchall():
|
||||
print(f" {row}")
|
||||
|
||||
conn.close()
|
||||
print("\n完成!")
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""修复 tixianjilu 导入 — 使用指定列名的 INSERT"""
|
||||
import re, sys, time, MySQLdb
|
||||
|
||||
sys.path.insert(0, '.')
|
||||
import os; os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
import django; django.setup()
|
||||
from app_secrets import DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME
|
||||
|
||||
SQL_FILE = os.path.join(os.path.dirname(__file__), 'xaio_cheng_xu_backup.sql')
|
||||
|
||||
with open(SQL_FILE, 'r', encoding='utf-8') as f:
|
||||
sql_content = f.read()
|
||||
|
||||
# 旧表字段顺序(SQL INSERT VALUES 的顺序)
|
||||
OLD_COLUMNS = [
|
||||
'id', 'yonghuid', 'avatar', 'phone', 'nicheng', 'leixing', 'zhifu',
|
||||
'skzhanghao', 'jine', 'zhuangtai', 'fangshi', 'shenheid', 'bhliyou',
|
||||
'create_time', 'update_time', 'shenhe_danhao', 'shenhe_jilu_id'
|
||||
]
|
||||
|
||||
# 新表字段顺序
|
||||
NEW_COLUMNS = [
|
||||
'id', 'yonghuid', 'avatar', 'phone', 'nicheng', 'leixing', 'zhifu',
|
||||
'skzhanghao', 'jine', 'zhuangtai', 'fangshi', 'shenheid', 'bhliyou',
|
||||
'shenhe_jilu_id', 'shenhe_danhao', 'create_time', 'update_time'
|
||||
]
|
||||
|
||||
def parse_sql_value(val_str):
|
||||
val_str = val_str.strip()
|
||||
if val_str.upper() == 'NULL': return None
|
||||
if val_str.startswith("'") and val_str.endswith("'"):
|
||||
return val_str[1:-1].replace("\\'", "'").replace("\\\\", "\\")
|
||||
try: return int(val_str)
|
||||
except ValueError:
|
||||
try: return float(val_str)
|
||||
except ValueError: return val_str
|
||||
|
||||
def split_sql_values(values_str):
|
||||
values, current, in_quote, escape = [], [], False, False
|
||||
for ch in values_str:
|
||||
if escape: current.append(ch); escape = False; continue
|
||||
if ch == '\\': current.append(ch); escape = True; continue
|
||||
if ch == "'": in_quote = not in_quote; current.append(ch); continue
|
||||
if ch == ',' and not in_quote: values.append(''.join(current).strip()); current = []; continue
|
||||
current.append(ch)
|
||||
if current: values.append(''.join(current).strip())
|
||||
return [parse_sql_value(v) for v in values]
|
||||
|
||||
pattern = re.compile(r"INSERT\s+INTO\s+`tixianjilu`\s+VALUES\s*\((.+?)\)\s*;", re.IGNORECASE | re.DOTALL)
|
||||
matches = list(pattern.finditer(sql_content))
|
||||
print(f"找到 {len(matches)} 条 tixianjilu 记录")
|
||||
|
||||
conn = MySQLdb.connect(
|
||||
host=DATABASE_HOST, port=int(DATABASE_PORT),
|
||||
user=DATABASE_USER, password=DATABASE_PASSWORD,
|
||||
database=DATABASE_NAME, charset='utf8mb4',
|
||||
connect_timeout=10, read_timeout=60, write_timeout=60
|
||||
)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 0')
|
||||
|
||||
success = 0
|
||||
fail = 0
|
||||
start = time.time()
|
||||
|
||||
for i, m in enumerate(matches):
|
||||
vals = split_sql_values(m.group(1))
|
||||
if len(vals) != len(OLD_COLUMNS):
|
||||
fail += 1
|
||||
continue
|
||||
|
||||
# 旧字段 → 值映射
|
||||
old_dict = dict(zip(OLD_COLUMNS, vals))
|
||||
|
||||
# 按新字段顺序构建值列表
|
||||
new_vals = []
|
||||
for col in NEW_COLUMNS:
|
||||
new_vals.append(old_dict.get(col))
|
||||
|
||||
# 构建 INSERT 语句(指定列名)
|
||||
col_names = ', '.join(f'`{c}`' for c in NEW_COLUMNS)
|
||||
val_parts = []
|
||||
for v in new_vals:
|
||||
if v is None:
|
||||
val_parts.append('NULL')
|
||||
elif isinstance(v, str):
|
||||
escaped = conn.escape_string(v)
|
||||
if isinstance(escaped, bytes): escaped = escaped.decode()
|
||||
val_parts.append(f"'{escaped}'")
|
||||
elif isinstance(v, bool):
|
||||
val_parts.append('1' if v else '0')
|
||||
elif isinstance(v, (int, float)):
|
||||
val_parts.append(str(v))
|
||||
else:
|
||||
val_parts.append('NULL')
|
||||
|
||||
sql = f"INSERT INTO `tixianjilu` ({col_names}) VALUES ({', '.join(val_parts)})"
|
||||
try:
|
||||
cursor.execute(sql)
|
||||
success += 1
|
||||
except Exception as e:
|
||||
fail += 1
|
||||
if fail <= 3:
|
||||
print(f" ERR: {str(e)[:100]}")
|
||||
|
||||
if success % 100 == 0 and success > 0:
|
||||
conn.commit()
|
||||
|
||||
if (i + 1) % 200 == 0:
|
||||
elapsed = time.time() - start
|
||||
pct = (i + 1) / len(matches)
|
||||
sys.stdout.write(f'\r [{pct:5.1%}] {i+1}/{len(matches)} {elapsed:.0f}s OK:{success} FAIL:{fail}')
|
||||
sys.stdout.flush()
|
||||
|
||||
conn.commit()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 1')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
elapsed = time.time() - start
|
||||
print(f'\n完成: 成功 {success}, 失败 {fail}, 耗时 {elapsed:.0f}s')
|
||||
145
import_backup.py
145
import_backup.py
@@ -1,145 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""只导入用户迁移所需的表到临时数据库 xaio_old"""
|
||||
import MySQLdb
|
||||
import re
|
||||
from app_secrets import DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD
|
||||
|
||||
# 需要导入的表(用户相关)
|
||||
NEEDED_TABLES = {
|
||||
'user_main', 'user_dashou', 'user_guanshi', 'user_kefu',
|
||||
'user_shangjia', 'user_boss', 'admin_profile', 'user_zuzhang',
|
||||
'user_shenheguan', 'user_role', 'role', 'permission',
|
||||
'role_permission', 'account_permission',
|
||||
}
|
||||
|
||||
def get_conn(db=None):
|
||||
kwargs = dict(
|
||||
host=DATABASE_HOST, port=int(DATABASE_PORT),
|
||||
user=DATABASE_USER, password=DATABASE_PASSWORD,
|
||||
charset='utf8mb4', connect_timeout=60,
|
||||
read_timeout=300, write_timeout=300,
|
||||
)
|
||||
if db:
|
||||
kwargs['database'] = db
|
||||
return MySQLdb.connect(**kwargs)
|
||||
|
||||
# Drop and recreate
|
||||
conn = get_conn()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('DROP DATABASE IF EXISTS xaio_old')
|
||||
cursor.execute('CREATE DATABASE xaio_old CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci')
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print('临时数据库 xaio_old 已重建')
|
||||
|
||||
# Read the SQL file
|
||||
with open('xaio_cheng_xu_backup.sql', 'r', encoding='utf-8') as f:
|
||||
sql_content = f.read()
|
||||
|
||||
# Parse and filter statements
|
||||
# We need CREATE TABLE and INSERT statements for NEEDED_TABLES only
|
||||
# Also need DROP TABLE IF EXISTS for those tables
|
||||
statements = []
|
||||
current = []
|
||||
current_table = None
|
||||
in_create = False
|
||||
|
||||
for line in sql_content.split('\n'):
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith('--') or stripped.startswith('/*'):
|
||||
continue
|
||||
|
||||
# Detect table name from CREATE TABLE or DROP TABLE or INSERT INTO
|
||||
create_match = re.match(r'CREATE\s+TABLE\s+`(\w+)`', stripped, re.IGNORECASE)
|
||||
drop_match = re.match(r'DROP\s+TABLE\s+IF\s+EXISTS\s+`(\w+)`', stripped, re.IGNORECASE)
|
||||
insert_match = re.match(r'INSERT\s+INTO\s+`(\w+)`', stripped, re.IGNORECASE)
|
||||
|
||||
if create_match:
|
||||
current_table = create_match.group(1)
|
||||
in_create = True
|
||||
elif drop_match:
|
||||
current_table = drop_match.group(1)
|
||||
elif insert_match:
|
||||
current_table = insert_match.group(1)
|
||||
in_create = False
|
||||
|
||||
# Only include statements for needed tables
|
||||
if current_table and current_table in NEEDED_TABLES:
|
||||
current.append(line)
|
||||
|
||||
if stripped.endswith(';'):
|
||||
stmt = '\n'.join(current).strip()
|
||||
current = []
|
||||
current_table = None
|
||||
in_create = False
|
||||
upper = stmt.upper()
|
||||
if not upper.startswith('SET ') and not upper.startswith('USE '):
|
||||
statements.append(stmt)
|
||||
elif stripped.endswith(';'):
|
||||
# Not a needed table, skip
|
||||
current = []
|
||||
current_table = None
|
||||
in_create = False
|
||||
elif current_table and current_table in NEEDED_TABLES:
|
||||
current.append(line)
|
||||
else:
|
||||
# Not needed, skip but track state
|
||||
if stripped.endswith(';'):
|
||||
current = []
|
||||
current_table = None
|
||||
|
||||
print(f'筛选出 {len(statements)} 条用户相关SQL语句')
|
||||
|
||||
# Execute
|
||||
conn = get_conn(db='xaio_old')
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 0')
|
||||
executed = 0
|
||||
errors = 0
|
||||
|
||||
for stmt in statements:
|
||||
try:
|
||||
cursor.execute(stmt)
|
||||
executed += 1
|
||||
if executed % 20 == 0:
|
||||
conn.commit()
|
||||
except MySQLdb.OperationalError as e:
|
||||
if e.args[0] in (2006, 2013):
|
||||
try:
|
||||
conn.close()
|
||||
except:
|
||||
pass
|
||||
conn = get_conn(db='xaio_old')
|
||||
cursor = conn.cursor()
|
||||
try:
|
||||
cursor.execute(stmt)
|
||||
executed += 1
|
||||
except Exception as e2:
|
||||
errors += 1
|
||||
if errors <= 5:
|
||||
print(f' ERR(retry): {str(e2)[:100]}')
|
||||
else:
|
||||
errors += 1
|
||||
if errors <= 5:
|
||||
print(f' ERR: {str(e)[:100]}')
|
||||
except Exception as e:
|
||||
errors += 1
|
||||
if errors <= 5:
|
||||
print(f' ERR: {str(e)[:100]}')
|
||||
|
||||
conn.commit()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 1')
|
||||
conn.commit()
|
||||
print(f'\n导入完成: 成功 {executed}, 错误 {errors}')
|
||||
|
||||
# Check row counts
|
||||
print('\n关键表行数:')
|
||||
for table in sorted(NEEDED_TABLES):
|
||||
try:
|
||||
cursor.execute('SELECT COUNT(*) FROM `%s`' % table)
|
||||
count = cursor.fetchone()[0]
|
||||
print(f' {table}: {count} rows')
|
||||
except Exception:
|
||||
print(f' {table}: NOT FOUND')
|
||||
|
||||
conn.close()
|
||||
463
migrate_data.py
463
migrate_data.py
@@ -1,463 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
migrate_data.py — 从 SQL 备份文件直接迁移数据到新的 gvsdsdk User + RBAC 体系
|
||||
v3: 正确的字段映射 + 基于扩展表存在性分配角色 + bulk_create + 实时进度
|
||||
"""
|
||||
import os, sys, re, uuid, time, datetime, MySQLdb
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'a_long_dianjing.settings')
|
||||
import django; django.setup()
|
||||
|
||||
from app_secrets import DATABASE_HOST, DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, DATABASE_NAME
|
||||
|
||||
TENANT_UUID = uuid.UUID('a0000000-0000-0000-0000-000000000001').bytes
|
||||
COMPANY_UUID = uuid.UUID('b0000000-0000-0000-0000-000000000001').bytes
|
||||
BATCH_SIZE = 500
|
||||
|
||||
ROLE_MAP = {
|
||||
'normal': '消费者', 'dashou': '打手', 'shop': '商家',
|
||||
'guanshi': '负责人', 'admin': '管理员', 'kefu': '客服',
|
||||
'zuzhang': '组长', 'shenheguan': '审核官',
|
||||
}
|
||||
|
||||
# 角色优先级(高→低): 管理员 > 客服 > 负责人 > 组长 > 审核官 > 商家 > 打手 > 消费者
|
||||
ROLE_PRIORITY = ['admin', 'kefu', 'guanshi', 'zuzhang', 'shenheguan', 'shop', 'dashou', 'normal']
|
||||
|
||||
# 扩展表 → 角色类型 映射
|
||||
EXT_ROLE_MAP = {
|
||||
'admin_profile': 'admin',
|
||||
'user_kefu': 'kefu',
|
||||
'user_guanshi': 'guanshi',
|
||||
'user_zuzhang': 'zuzhang',
|
||||
'user_shenheguan': 'shenheguan',
|
||||
'user_shangjia': 'shop',
|
||||
'user_dashou': 'dashou',
|
||||
}
|
||||
|
||||
SQL_FILE = os.path.join(os.path.dirname(__file__), 'xaio_cheng_xu_backup.sql')
|
||||
|
||||
|
||||
class Progress:
|
||||
def __init__(self, total, width=35):
|
||||
self.total = total; self.width = width; self.start = time.time(); self.done = 0
|
||||
def update(self, n=1):
|
||||
self.done += n; pct = self.done / self.total if self.total else 0
|
||||
filled = int(self.width * pct); bar = '█' * filled + '░' * (self.width - filled)
|
||||
elapsed = time.time() - self.start
|
||||
eta = (elapsed / self.done * (self.total - self.done)) if self.done else 0
|
||||
sys.stdout.write(f'\r [{bar}] {pct:5.1%} {self.done}/{self.total} {elapsed:.0f}s ETA{eta:.0f}s')
|
||||
sys.stdout.flush()
|
||||
def finish(self):
|
||||
elapsed = time.time() - self.start
|
||||
sys.stdout.write(f'\r [{"█"*self.width}] 100% {self.done}/{self.total} {elapsed:.0f}s\n')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
def parse_insert_values(table_name, sql_content):
|
||||
pattern = re.compile(r"INSERT\s+INTO\s+`" + re.escape(table_name) + r"`\s+VALUES\s*\((.+?)\)\s*;", re.IGNORECASE | re.DOTALL)
|
||||
return [m.group(1) for m in pattern.finditer(sql_content)]
|
||||
|
||||
|
||||
def parse_sql_value(val_str):
|
||||
val_str = val_str.strip()
|
||||
if val_str.upper() == 'NULL': return None
|
||||
if val_str.startswith("'") and val_str.endswith("'"):
|
||||
return val_str[1:-1].replace("\\'", "'").replace("\\\\", "\\")
|
||||
try: return int(val_str)
|
||||
except ValueError:
|
||||
try: return float(val_str)
|
||||
except ValueError: return val_str
|
||||
|
||||
|
||||
def split_sql_values(values_str):
|
||||
values, current, in_quote, escape = [], [], False, False
|
||||
for ch in values_str:
|
||||
if escape: current.append(ch); escape = False; continue
|
||||
if ch == '\\': current.append(ch); escape = True; continue
|
||||
if ch == "'": in_quote = not in_quote; current.append(ch); continue
|
||||
if ch == ',' and not in_quote: values.append(''.join(current).strip()); current = []; continue
|
||||
current.append(ch)
|
||||
if current: values.append(''.join(current).strip())
|
||||
return [parse_sql_value(v) for v in values]
|
||||
|
||||
|
||||
def parse_dt(v):
|
||||
if isinstance(v, str) and v:
|
||||
try: return datetime.datetime.fromisoformat(v.replace(' ', 'T'))
|
||||
except: pass
|
||||
return datetime.datetime.utcnow()
|
||||
|
||||
|
||||
def get_raw_conn(db=None):
|
||||
kwargs = dict(host=DATABASE_HOST, port=int(DATABASE_PORT), user=DATABASE_USER,
|
||||
password=DATABASE_PASSWORD, charset='utf8mb4', connect_timeout=10,
|
||||
read_timeout=60, write_timeout=60)
|
||||
if db: kwargs['database'] = db
|
||||
return MySQLdb.connect(**kwargs)
|
||||
|
||||
|
||||
def run_migration():
|
||||
from gvsdsdk.models import User, Role, UserRole
|
||||
from users.models import (
|
||||
UserBoss, UserDashou, UserShangjia, UserGuanshi,
|
||||
UserZuzhang, UserShenheguan, AdminProfile, UserKefu,
|
||||
UserSensitiveData
|
||||
)
|
||||
|
||||
print("=" * 60)
|
||||
print("开始从 SQL 备份迁移数据到 gvsdsdk.User + RBAC")
|
||||
print("=" * 60)
|
||||
|
||||
# [0] 读取 SQL
|
||||
print("\n[0/7] 读取 SQL 备份文件...", end=' ')
|
||||
with open(SQL_FILE, 'r', encoding='utf-8') as f:
|
||||
sql_content = f.read()
|
||||
print(f"{len(sql_content)/1024/1024:.1f} MB")
|
||||
|
||||
# [1] 解析所有扩展表的 user_id → 建立角色映射
|
||||
print("\n[1/7] 解析扩展表建立角色映射...")
|
||||
|
||||
# 正确的 user_id 索引位置
|
||||
ext_user_id_idx = {
|
||||
'user_dashou': 22,
|
||||
'user_guanshi': 11,
|
||||
'user_kefu': 9,
|
||||
'user_shangjia': 14,
|
||||
'user_boss': 6,
|
||||
'admin_profile': 4,
|
||||
'user_zuzhang': 17,
|
||||
'user_shenheguan': 9,
|
||||
}
|
||||
|
||||
# user_main.id → 角色集合
|
||||
user_roles = {} # old_user_id -> set of role_types
|
||||
|
||||
for table_name, role_type in EXT_ROLE_MAP.items():
|
||||
inserts = parse_insert_values(table_name, sql_content)
|
||||
uid_idx = ext_user_id_idx[table_name]
|
||||
count = 0
|
||||
for values_str in inserts:
|
||||
vals = split_sql_values(values_str)
|
||||
if uid_idx < len(vals) and vals[uid_idx] is not None:
|
||||
old_uid = vals[uid_idx]
|
||||
if old_uid not in user_roles:
|
||||
user_roles[old_uid] = set()
|
||||
user_roles[old_uid].add(role_type)
|
||||
count += 1
|
||||
print(f" {table_name}: {count} 条有 user_id")
|
||||
|
||||
# [2] 解析 user_main
|
||||
print("\n[2/7] 解析 user_main...")
|
||||
user_main_inserts = parse_insert_values('user_main', sql_content)
|
||||
total = len(user_main_inserts)
|
||||
print(f" 共 {total} 条记录")
|
||||
|
||||
uid_map = {} # yonghuid -> UserUUID (bytes)
|
||||
user_id_map = {} # old id -> yonghuid
|
||||
phone_to_yonghuid = {}
|
||||
|
||||
# 先解析所有 user_main 记录
|
||||
user_data = [] # (old_id, yonghuid, fields_dict, role_type)
|
||||
prog = Progress(total)
|
||||
|
||||
for i, values_str in enumerate(user_main_inserts):
|
||||
vals = split_sql_values(values_str)
|
||||
if len(vals) < 16:
|
||||
prog.update(); continue
|
||||
|
||||
old_id = vals[1]
|
||||
yonghuid = str(vals[2]) if vals[2] else None
|
||||
if not yonghuid:
|
||||
prog.update(); continue
|
||||
|
||||
# 确定角色:基于扩展表存在性 + user_type
|
||||
roles = user_roles.get(old_id, set())
|
||||
user_type = str(vals[11]) if vals[11] else 'normal'
|
||||
|
||||
# user_type 也可能指定角色
|
||||
if user_type == 'admin':
|
||||
roles.add('admin')
|
||||
elif user_type == 'kefu':
|
||||
roles.add('kefu')
|
||||
|
||||
# 如果没有任何角色,默认为 normal
|
||||
if not roles:
|
||||
roles.add('normal')
|
||||
|
||||
# 选择优先级最高的角色
|
||||
primary_role = 'normal'
|
||||
for r in ROLE_PRIORITY:
|
||||
if r in roles:
|
||||
primary_role = r
|
||||
break
|
||||
|
||||
user_data.append((old_id, yonghuid, vals, primary_role, roles))
|
||||
user_id_map[old_id] = yonghuid
|
||||
phone = str(vals[5]) if vals[5] else ''
|
||||
if phone: phone_to_yonghuid[phone] = yonghuid
|
||||
prog.update()
|
||||
|
||||
prog.finish()
|
||||
print(f" 解析完成: {len(user_data)} 条有效记录")
|
||||
|
||||
# [3] 创建角色
|
||||
print("\n[3/7] 创建角色...")
|
||||
created_roles = {}
|
||||
for user_type, role_name in ROLE_MAP.items():
|
||||
role, created = Role.objects.get_or_create(
|
||||
RoleName=role_name, TenantUUID=TENANT_UUID,
|
||||
defaults={'RoleUUID': uuid.uuid4().bytes, 'RoleDesc': f'{role_name}角色', 'RoleStatus': 1, 'AssignScope': 0})
|
||||
created_roles[user_type] = role
|
||||
print(f" {role_name}: {'NEW' if created else 'EXISTS'}")
|
||||
|
||||
# [4] 批量创建 User + UserRole + UserSensitiveData
|
||||
print("\n[4/7] 批量创建 User + UserRole + UserSensitiveData...")
|
||||
|
||||
user_batch, ur_batch, usd_batch = [], [], []
|
||||
errors = 0
|
||||
prog = Progress(len(user_data))
|
||||
|
||||
for old_id, yonghuid, vals, primary_role, all_roles in user_data:
|
||||
openid = str(vals[3]) if vals[3] else ''
|
||||
avatar = str(vals[4]) if vals[4] else 'a_long/morentouxiang.jpg'
|
||||
phone = str(vals[5]) if vals[5] else ''
|
||||
password = str(vals[6]) if vals[6] else ''
|
||||
zhifu = str(vals[7]) if vals[7] else ''
|
||||
skzhanghao = str(vals[8]) if vals[8] else ''
|
||||
ip = str(vals[9]) if vals[9] else ''
|
||||
last_login_time = vals[10]
|
||||
create_time = vals[12]
|
||||
unionid = str(vals[14]) if vals[14] else ''
|
||||
shoujihao_renzheng = bool(vals[15]) if vals[15] is not None else False
|
||||
|
||||
user_uuid = uuid.uuid4().bytes
|
||||
ct, lt = parse_dt(create_time), parse_dt(last_login_time)
|
||||
|
||||
user_batch.append(User(
|
||||
UserUUID=user_uuid, UserName=yonghuid, UserUID=yonghuid,
|
||||
OpenID=openid, UnionID=unionid, Avatar=avatar, Phone=phone,
|
||||
PhoneVerified=shoujihao_renzheng, IP=ip, UserAccountLicense=1,
|
||||
UserPositionStatus=0, UserCreateTime=ct, UserLastLoginDate=lt,
|
||||
IsStaff=('admin' in all_roles), IsSuperuser=('admin' in all_roles)))
|
||||
|
||||
# 为每个角色创建 UserRole(多对多)
|
||||
for role_type in all_roles:
|
||||
role = created_roles.get(role_type)
|
||||
if role:
|
||||
ur_batch.append(UserRole(
|
||||
UserRoleUUID=uuid.uuid4().bytes, UserUUID=user_uuid,
|
||||
RoleUUID=role.RoleUUID, CompanyUUID=COMPANY_UUID))
|
||||
|
||||
usd_batch.append(UserSensitiveData(
|
||||
user_uuid_id=user_uuid, password=password, zhifu=zhifu,
|
||||
skzhanghao=skzhanghao, shoujihao_renzheng=shoujihao_renzheng))
|
||||
|
||||
uid_map[yonghuid] = user_uuid
|
||||
|
||||
if len(user_batch) >= BATCH_SIZE:
|
||||
try:
|
||||
User.objects.bulk_create(user_batch, ignore_conflicts=True)
|
||||
UserRole.objects.bulk_create(ur_batch, ignore_conflicts=True)
|
||||
UserSensitiveData.objects.bulk_create(usd_batch, ignore_conflicts=True)
|
||||
except Exception: errors += len(user_batch)
|
||||
user_batch, ur_batch, usd_batch = [], [], []
|
||||
prog.update()
|
||||
|
||||
if user_batch:
|
||||
try:
|
||||
User.objects.bulk_create(user_batch, ignore_conflicts=True)
|
||||
UserRole.objects.bulk_create(ur_batch, ignore_conflicts=True)
|
||||
UserSensitiveData.objects.bulk_create(usd_batch, ignore_conflicts=True)
|
||||
except Exception: errors += len(user_batch)
|
||||
|
||||
prog.finish()
|
||||
print(f" 成功: {len(uid_map)}, 错误: {errors}")
|
||||
|
||||
# [5] 迁移扩展表 — 用 ORM bulk_create(正确字段映射)
|
||||
print("\n[5/7] 迁移扩展表...")
|
||||
|
||||
# 正确的字段映射: (sql_index, model_field_name)
|
||||
# None = 跳过, '_user_id' = FK 到新 User
|
||||
ext_configs = [
|
||||
('user_dashou', UserDashou, [
|
||||
(0, None), (1, 'nicheng'), (2, 'chenghao'), (3, 'zhuangtai'), (4, 'zaixianzhuangtai'),
|
||||
(5, 'zhanghaozhuangtai'), (6, 'jieshao'), (7, 'jiedanzongliang'), (8, 'chengjiaozongliang'),
|
||||
(9, 'tuikuanliang'), (10, 'yue'), (11, 'zonge'), (12, 'dianhua'), (13, 'wechat'),
|
||||
(14, 'yaoqingren'), (15, 'jinrijiedan'), (16, 'jinrishouyi'), (17, 'jinyuejiedan'),
|
||||
(18, 'jinyueshouyi'), (19, 'jifen'), (20, 'yajin'), (21, None), # create_time
|
||||
(22, '_user_id'), # user_id FK
|
||||
(23, 'ewai_xiane'), (24, 'jinritixian_jine'), (25, 'kaioi_ewai_xiane'),
|
||||
(26, 'last_tixian_date'),
|
||||
]),
|
||||
('user_guanshi', UserGuanshi, [
|
||||
(0, None), (1, 'yaoqingma'), (2, 'dianhua'), (3, 'wechat'),
|
||||
(4, 'yaogingshuliang'), (5, 'zhuangtai'), (6, 'jinrichongzhi'),
|
||||
(7, 'jinyuechongzhi'), (8, 'chongzhifenrun'), (9, 'yue'), (10, None), # create_time
|
||||
(11, '_user_id'),
|
||||
(12, 'fenghong_erci'), (13, 'fenghong_erci_enabled'), (14, 'fenhong_yici'),
|
||||
(15, 'ewai_xiane'), (16, 'jinritixian_jine'), (17, 'kaioi_ewai_xiane'),
|
||||
(18, 'last_tixian_date'), (19, 'yaoqingren'), (20, 'invite_qrcode_url'),
|
||||
]),
|
||||
('user_kefu', UserKefu, [
|
||||
(0, None), (1, 'nicheng'), (2, 'erjimima'), (3, 'zhuangtai'),
|
||||
(4, 'jinrichuli'), (5, 'jinyuechuli'), (6, 'zongchuli'),
|
||||
(7, None), (8, None), (9, '_user_id'),
|
||||
]),
|
||||
('user_shangjia', UserShangjia, [
|
||||
(0, None), (1, 'nicheng'), (2, 'zhuangtai'), (3, 'dianhua'), (4, 'wechat'),
|
||||
(5, 'fabu'), (6, 'tuikuan'), (7, 'yue'), (8, 'chengjiao'),
|
||||
(9, 'jinridingdan'), (10, 'jinriliushui'), (11, 'jinyuedingdan'),
|
||||
(12, 'jinyueliushui'), (13, None), (14, '_user_id'),
|
||||
]),
|
||||
('user_boss', UserBoss, [
|
||||
(0, None), (1, 'nickname'), (2, 'zonge'), (3, 'alldingdan'),
|
||||
(4, 'alltui'), (5, None), (6, '_user_id'),
|
||||
]),
|
||||
('admin_profile', AdminProfile, [
|
||||
(0, None), (1, 'password'), (2, 'wechat'), (3, None),
|
||||
(4, '_user_id'), (5, 'yaoqingma'),
|
||||
]),
|
||||
('user_zuzhang', UserZuzhang, [
|
||||
(0, None), (1, 'yaoqingma'), (2, 'fenyong_zonge'), (3, 'ketixian_jine'),
|
||||
(4, 'yaoqing_zongshu'), (5, 'jinri_fenyong'), (6, 'jinyue_fenyong'),
|
||||
(7, 'zhuangtai'), (8, 'haibao_url'), (9, 'jinri_tixian'), (10, 'last_tixian_time'),
|
||||
(11, 'ewai_tixian_xiane'), (12, 'kaioi_ewai_tixian'), (13, 'kaioi_ewai_fenhong'),
|
||||
(14, 'ewai_fenhong_jine'), (15, None), (16, None), (17, '_user_id'),
|
||||
]),
|
||||
('user_shenheguan', UserShenheguan, [
|
||||
(0, None), (1, 'zhuangtai'), (2, 'shenhe_zhuangtai'),
|
||||
(3, 'shenhe_zongshu'), (4, 'tongguo_zongshu'), (5, 'yue'), (6, 'zonge'),
|
||||
(7, None), (8, None), (9, '_user_id'), (10, 'is_renzheng'),
|
||||
]),
|
||||
]
|
||||
|
||||
for table_name, model, field_map in ext_configs:
|
||||
inserts = parse_insert_values(table_name, sql_content)
|
||||
if not inserts:
|
||||
print(f" {table_name}: 0 条"); continue
|
||||
|
||||
user_id_sql_idx = next((idx for idx, fn in field_map if fn == '_user_id'), None)
|
||||
if user_id_sql_idx is None:
|
||||
print(f" {table_name}: 无法定位 user_id"); continue
|
||||
|
||||
batch = []
|
||||
success = 0
|
||||
fail = 0
|
||||
prog = Progress(len(inserts))
|
||||
|
||||
for values_str in inserts:
|
||||
vals = split_sql_values(values_str)
|
||||
if not vals or user_id_sql_idx >= len(vals) or vals[user_id_sql_idx] is None:
|
||||
fail += 1; prog.update(); continue
|
||||
|
||||
old_user_id = vals[user_id_sql_idx]
|
||||
yonghuid = user_id_map.get(old_user_id)
|
||||
if not yonghuid:
|
||||
fail += 1; prog.update(); continue
|
||||
|
||||
new_uuid = uid_map.get(yonghuid)
|
||||
if not new_uuid:
|
||||
fail += 1; prog.update(); continue
|
||||
|
||||
try:
|
||||
obj = model(user_id=new_uuid)
|
||||
for sql_idx, field_name in field_map:
|
||||
if field_name is None or field_name == '_user_id':
|
||||
continue
|
||||
if sql_idx < len(vals) and vals[sql_idx] is not None:
|
||||
try: setattr(obj, field_name, vals[sql_idx])
|
||||
except: pass
|
||||
batch.append(obj)
|
||||
success += 1
|
||||
except Exception:
|
||||
fail += 1
|
||||
|
||||
if len(batch) >= BATCH_SIZE:
|
||||
try: model.objects.bulk_create(batch, ignore_conflicts=True)
|
||||
except: pass
|
||||
batch = []
|
||||
prog.update()
|
||||
|
||||
if batch:
|
||||
try: model.objects.bulk_create(batch, ignore_conflicts=True)
|
||||
except: pass
|
||||
|
||||
prog.finish()
|
||||
print(f" 成功: {success}, 失败(无user_id): {fail}")
|
||||
|
||||
# [6] 迁移其他引用表
|
||||
print("\n[6/7] 迁移其他引用 yonghuid 的表...")
|
||||
other_tables = [
|
||||
'tixianjilu', 'tixian_shenhe_jilu', 'tixian_auto_record',
|
||||
'xiugaijilu', 'ranking_record',
|
||||
]
|
||||
|
||||
conn = get_raw_conn(db=DATABASE_NAME)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 0')
|
||||
|
||||
for table_name in other_tables:
|
||||
inserts = parse_insert_values(table_name, sql_content)
|
||||
if not inserts:
|
||||
print(f" {table_name}: 0 条"); continue
|
||||
|
||||
success = 0
|
||||
prog = Progress(len(inserts))
|
||||
|
||||
for values_str in inserts:
|
||||
sql = f"INSERT INTO `{table_name}` VALUES ({values_str})"
|
||||
try:
|
||||
cursor.execute(sql)
|
||||
success += 1
|
||||
except Exception:
|
||||
pass
|
||||
if success % 100 == 0 and success > 0:
|
||||
try: conn.commit()
|
||||
except: pass
|
||||
prog.update()
|
||||
|
||||
try: conn.commit()
|
||||
except MySQLdb.OperationalError:
|
||||
conn = get_raw_conn(db=DATABASE_NAME)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 0')
|
||||
|
||||
prog.finish()
|
||||
print(f" 成功: {success}")
|
||||
|
||||
try:
|
||||
cursor.execute('SET FOREIGN_KEY_CHECKS = 1')
|
||||
conn.commit(); conn.close()
|
||||
except: pass
|
||||
|
||||
# [7] 验证
|
||||
print("\n[7/7] 验证迁移结果...")
|
||||
new_user_count = User.objects.count()
|
||||
new_ur_count = UserRole.objects.count()
|
||||
usd_count = UserSensitiveData.objects.count()
|
||||
|
||||
print(f" gvsdsdk.User: {new_user_count}")
|
||||
print(f" UserRole: {new_ur_count}")
|
||||
print(f" UserSensitiveData: {usd_count}")
|
||||
|
||||
print("\n 角色分布:")
|
||||
for user_type, role in created_roles.items():
|
||||
ur_count = UserRole.objects.filter(RoleUUID=role.RoleUUID).count()
|
||||
print(f" {ROLE_MAP[user_type]}: {ur_count} 人")
|
||||
|
||||
print("\n 扩展表:")
|
||||
for name, model in [('user_dashou', UserDashou), ('user_guanshi', UserGuanshi),
|
||||
('user_kefu', UserKefu), ('user_shangjia', UserShangjia),
|
||||
('user_boss', UserBoss), ('admin_profile', AdminProfile),
|
||||
('user_zuzhang', UserZuzhang), ('user_shenheguan', UserShenheguan)]:
|
||||
print(f" {name}: {model.objects.count()} 条")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("迁移完成!")
|
||||
print(f"TENANT_UUID = {uuid.UUID(bytes=TENANT_UUID)}")
|
||||
print(f"COMPANY_UUID = {uuid.UUID(bytes=COMPANY_UUID)}")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_migration()
|
||||
@@ -1,13 +0,0 @@
|
||||
import MySQLdb
|
||||
|
||||
conn = MySQLdb.connect(
|
||||
host='gvsds.com', port=50030, user='root',
|
||||
password='sajksh.sdfGH3YUge.wjkd+',
|
||||
database='xaio_cheng_xu', charset='utf8mb4'
|
||||
)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SHOW TABLES")
|
||||
for row in cur.fetchall():
|
||||
print(row[0])
|
||||
cur.close()
|
||||
conn.close()
|
||||
@@ -1090,7 +1090,7 @@ class ShangJiaXinXiView(APIView):
|
||||
|
||||
# ------------------- 3. 从 ShangjiaRiTongji 获取今日统计 -------------------
|
||||
try:
|
||||
today_stat = ShangjiaRiTongji.query.get(UserUID=yonghuid, riqi=today)
|
||||
today_stat = ShangjiaRiTongji.query.get(yonghuid=yonghuid, riqi=today)
|
||||
jinri_paifa_dingdan = today_stat.paifa_dingdan_shu
|
||||
jinri_paifa_jine = float(today_stat.paifa_jine)
|
||||
jinri_tuikuan_dingdan = today_stat.tuikuan_dingdan_shu
|
||||
@@ -1105,7 +1105,7 @@ class ShangJiaXinXiView(APIView):
|
||||
|
||||
# ------------------- 4. 从 ShangjiaRiTongji 获取本月统计 -------------------
|
||||
month_stats = ShangjiaRiTongji.query.filter(
|
||||
UserUID=yonghuid,
|
||||
yonghuid=yonghuid,
|
||||
riqi__gte=this_month_start,
|
||||
riqi__lte=today
|
||||
).aggregate(
|
||||
@@ -2170,7 +2170,7 @@ class TixianJiluHuoquViewV2(APIView):
|
||||
limit = 100
|
||||
offset = (page - 1) * limit
|
||||
|
||||
qs = Tixianjilu.query.filter(UserUID=yonghuid)
|
||||
qs = Tixianjilu.query.filter(yonghuid=yonghuid)
|
||||
if mode == 2:
|
||||
# 自动提现:含新审核关联记录;zhuangtai>0 时按状态筛选
|
||||
if zhuangtai_filter > 0:
|
||||
@@ -5739,7 +5739,7 @@ class YonghuTixianShenheXiugaiView(APIView):
|
||||
try:
|
||||
tixian_record = Tixianjilu.objects.select_for_update().get(
|
||||
id=tixian_id,
|
||||
UserUID=current_yonghuid # 确保只能修改自己的记录
|
||||
yonghuid=current_yonghuid # 确保只能修改自己的记录
|
||||
)
|
||||
except Tixianjilu.DoesNotExist:
|
||||
return Response({
|
||||
@@ -6167,7 +6167,7 @@ class ChufaJiluHuoquView(APIView):
|
||||
zhengju_mapping = {}
|
||||
if dingdan_ids and shangjia_ids:
|
||||
# 构建Q对象:dingdan_id在列表中 AND yonghuid在商家ID列表中
|
||||
zhengju_q = Q(dingdan_id__in=dingdan_ids, UserUID__in=shangjia_ids)
|
||||
zhengju_q = Q(dingdan_id__in=dingdan_ids, yonghuid__in=shangjia_ids)
|
||||
zhengju_queryset = Chufatupian.query.filter(zhengju_q).values('dingdan_id', 'yonghuid', 'tupian')
|
||||
|
||||
for item in zhengju_queryset:
|
||||
@@ -6362,7 +6362,7 @@ class DashouShensuView(APIView):
|
||||
# 检查是否已经有申诉图片
|
||||
existing_shensu_tupian = Chufatupian.query.filter(
|
||||
dingdan_id=chufa_record.dingdan_id,
|
||||
UserUID=yonghuid
|
||||
yonghuid=yonghuid
|
||||
).exists()
|
||||
|
||||
if existing_shensu_tupian:
|
||||
@@ -6609,7 +6609,7 @@ class ShangjiaChufaJiluHuoquView(APIView):
|
||||
shensu_mapping = {}
|
||||
if dingdan_ids and dashou_ids:
|
||||
# 构建Q对象:dingdan_id在列表中 AND yonghuid在打手ID列表中
|
||||
shensu_q = Q(dingdan_id__in=dingdan_ids, UserUID__in=dashou_ids)
|
||||
shensu_q = Q(dingdan_id__in=dingdan_ids, yonghuid__in=dashou_ids)
|
||||
shensu_queryset = Chufatupian.query.filter(shensu_q).values('dingdan_id', 'yonghuid', 'tupian')
|
||||
|
||||
for item in shensu_queryset:
|
||||
@@ -9198,11 +9198,11 @@ class KefuPunishmentDetailView(APIView):
|
||||
shensu_images = []
|
||||
try:
|
||||
# 商家证据图片:上传者为请求人 (qingqiuid)
|
||||
zhengju_qs = Chufatupian.query.filter(dingdan_id=dingdan_id, UserUID=record.qingqiuid)
|
||||
zhengju_qs = Chufatupian.query.filter(dingdan_id=dingdan_id, yonghuid=record.qingqiuid)
|
||||
zhengju_images = [item.tupian for item in zhengju_qs if item.tupian]
|
||||
|
||||
# 打手申诉图片:上传者为打手 (dashouid)
|
||||
shensu_qs = Chufatupian.query.filter(dingdan_id=dingdan_id, UserUID=record.dashouid)
|
||||
shensu_qs = Chufatupian.query.filter(dingdan_id=dingdan_id, yonghuid=record.dashouid)
|
||||
shensu_images = [item.tupian for item in shensu_qs if item.tupian]
|
||||
except Exception as e:
|
||||
logger.error(f"查询图片失败: {e}")
|
||||
@@ -11940,7 +11940,7 @@ class FaKuanPayPollView(APIView):
|
||||
if not dingdanid:
|
||||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||||
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, UserUID=request.user.yonghuid)
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.yonghuid)
|
||||
if order.zhuangtai == 3:
|
||||
return Response({'code': 200, 'message': '支付成功'})
|
||||
else:
|
||||
@@ -11963,7 +11963,7 @@ class FaKuanPayFailView(APIView):
|
||||
if not dingdanid:
|
||||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||||
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, UserUID=request.user.yonghuid)
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.yonghuid)
|
||||
if order.zhuangtai == 9: # 未支付
|
||||
order.delete()
|
||||
logger.info(f"用户{request.user.yonghuid}删除未支付订单: {dingdanid}")
|
||||
@@ -12375,7 +12375,7 @@ class KaohePayCallbackView(View):
|
||||
|
||||
# 从临时表获取业务参数并创建审核记录
|
||||
try:
|
||||
temp = KaohePayTemp.query.get(dingdan_id=out_trade_no, UserUID=order.yonghuid)
|
||||
temp = KaohePayTemp.query.get(dingdan_id=out_trade_no, yonghuid=order.yonghuid)
|
||||
logger.info(f"找到临时表记录: {out_trade_no}, 用户ID: {order.yonghuid}")
|
||||
|
||||
user = User.query.get(UserUID=order.yonghuid)
|
||||
@@ -12495,7 +12495,7 @@ class KaohePayPollView(APIView):
|
||||
if not dingdanid:
|
||||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||||
try:
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, UserUID=request.user.yonghuid, leixing=5)
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.yonghuid, leixing=5)
|
||||
if order.zhuangtai == 3:
|
||||
return Response({'code': 200, 'message': '支付成功'})
|
||||
return Response({'code': 400, 'message': '订单尚未支付'})
|
||||
@@ -12512,7 +12512,7 @@ class KaohePayFailView(APIView):
|
||||
if not dingdanid:
|
||||
return Response({'code': 400, 'message': '订单ID不能为空'})
|
||||
try:
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, UserUID=request.user.yonghuid, leixing=5)
|
||||
order = Czjilu.query.get(dingdan_id=dingdanid, yonghuid=request.user.yonghuid, leixing=5)
|
||||
if order.zhuangtai == 9:
|
||||
order.delete()
|
||||
return Response({'code': 200, 'message': '订单已删除'})
|
||||
@@ -12864,7 +12864,7 @@ class TixianQueRenAutoView(APIView):
|
||||
try:
|
||||
auto_record = TixianAutoRecord.objects.select_for_update().get(
|
||||
tixian_id=tixian_id,
|
||||
UserUID=user_main.yonghuid,
|
||||
yonghuid=user_main.yonghuid,
|
||||
)
|
||||
except TixianAutoRecord.DoesNotExist:
|
||||
return Response({'code': 4, 'msg': '提现记录不存在'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""验证所有兼容性修复"""
|
||||
import os; os.environ['DJANGO_SETTINGS_MODULE']='a_long_dianjing.settings'
|
||||
import django; django.setup()
|
||||
from gvsdsdk.models import User
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
from rest_framework_simplejwt.authentication import JWTAuthentication
|
||||
from unittest.mock import Mock
|
||||
|
||||
u = User.objects.filter(UserName__isnull=False).first()
|
||||
print(f'用户: {u.UserName}, UserUID: {u.UserUID}')
|
||||
|
||||
# 1. JWT 认证
|
||||
refresh = RefreshToken.for_user(u)
|
||||
access = refresh.access_token
|
||||
auth = JWTAuthentication()
|
||||
request = Mock()
|
||||
request.META = {'HTTP_AUTHORIZATION': f'Bearer {access}'}
|
||||
request._request = request
|
||||
validated_user, _ = auth.authenticate(request)
|
||||
print(f'1. JWT: {validated_user.UserName}')
|
||||
|
||||
# 2. 兼容属性
|
||||
print(f'2. yonghuid: {u.yonghuid}')
|
||||
print(f' phone: {u.phone}')
|
||||
print(f' avatar: {u.avatar}')
|
||||
print(f' ip: {u.ip}')
|
||||
print(f' last_login_time: {u.last_login_time}')
|
||||
print(f' shoujihao_renzheng: {u.shoujihao_renzheng}')
|
||||
print(f' zhifu: {u.zhifu}')
|
||||
print(f' skzhanghao: {u.skzhanghao}')
|
||||
|
||||
# 3. ORM 查询
|
||||
u2 = User.query.get(UserUID=u.UserUID)
|
||||
print(f'3. User.query.get(UserUID=): {u2.UserName}')
|
||||
|
||||
u3 = User.query.filter(OpenID=u.OpenID).first()
|
||||
print(f' User.query.filter(OpenID=): {u3.UserName}')
|
||||
|
||||
u4 = User.query.filter(Phone=u.Phone).first() if u.Phone else None
|
||||
print(f' User.query.filter(Phone=): {u4.UserName if u4 else "N/A"}')
|
||||
|
||||
# 4. user_type
|
||||
print(f'4. user_type: {u.user_type}')
|
||||
|
||||
# 5. has_role
|
||||
print(f'5. has_role(dashou): {u.has_role("dashou")}')
|
||||
|
||||
# 6. dashou_profile
|
||||
dp = u.dashou_profile
|
||||
print(f'6. dashou_profile: {dp}')
|
||||
|
||||
print('\n所有测试通过!')
|
||||
Reference in New Issue
Block a user