📦 最终完整脚本(已调试通过)
将以下代码保存为 ImpHalo.py,并按后续步骤配置后运行。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import csv
import os
import sys
import time
# ==================== 配置区 ====================
HALO_URL = "你的网站域名" # 你的 Halo 站点地址 (结尾无 /)
API_TOKEN = "pat_xxxxxxxxxxxxxxxxxxxx" # 个人令牌
DEFAULT_EMAIL_DOMAIN = "default.com" # 邮箱为空时自动补全的域名
# ================================================
API_URL = f"{HALO_URL}/apis/api.console.halo.run/v1alpha1/users"
HEADERS = {
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json"
}
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
CSV_PATH = os.path.join(SCRIPT_DIR, "users.csv")
def load_users():
if not os.path.exists(CSV_PATH):
print(f"❌ 找不到文件: {CSV_PATH}")
sys.exit(1)
users = []
with open(CSV_PATH, "r", encoding="utf-8-sig", errors="ignore") as f:
reader = csv.DictReader(f)
# 检查必填列头
if "username" not in reader.fieldnames or "password" not in reader.fieldnames:
print("❌ CSV 必须包含 'username' 和 'password' 列")
sys.exit(1)
for idx, row in enumerate(reader, start=2):
username = row.get("username", "").strip()
if not username:
print(f"⚠️ 第 {idx} 行 username 为空,已跳过")
continue
password = row.get("password", "").strip()
if not password:
print(f"⚠️ 第 {idx} 行 username={username} password 为空,已跳过")
continue
email = row.get("email", "").strip()
if not email:
email = f"{username}@{DEFAULT_EMAIL_DOMAIN}"
print(f"ℹ️ {username} 邮箱为空,已自动生成: {email}")
display_name = row.get("displayName", "").strip()
bio = row.get("description", "").strip() # CSV 列名保持 description,内部映射为 bio
role_raw = row.get("role", "").strip()
roles = [role_raw] if role_raw else []
user = {
"name": username, # API 要求显式 name 字段
"username": username,
"displayName": display_name,
"email": email,
"password": password,
"bio": bio, # Halo 用户简介字段为 bio
"roles": roles
}
users.append(user)
print(f"📄 已处理 {len(users)} 条有效用户数据\n")
return users
def create_user(user):
"""发送创建请求,返回 (是否成功, 错误信息)"""
try:
resp = requests.post(API_URL, json=user, headers=HEADERS, timeout=30)
# 200 和 201 都视为成功
if resp.status_code in (200, 201):
return True, ""
# 409 表示冲突(用户已存在)
elif resp.status_code == 409:
return True, "already exists"
else:
error_msg = resp.text.strip() or f"状态码 {resp.status_code}"
try:
err = resp.json()
if "message" in err:
error_msg = err["message"]
elif "detail" in err:
error_msg = err["detail"]
except:
pass
return False, error_msg
except requests.exceptions.RequestException as e:
return False, str(e)
def main():
print(f"🚀 开始导入用户到 {HALO_URL} ...\n")
users = load_users()
if not users:
sys.exit(0)
success = 0
exists = 0
fail = 0
total = len(users)
for i, user in enumerate(users, start=1):
username = user["username"]
print(f"[{i}/{total}] 正在导入: {username} ... ", end="")
ok, error = create_user(user)
if ok and error == "already exists":
print("ℹ️ 已存在,跳过")
exists += 1
elif ok:
print("✅ 成功")
success += 1
else:
print(f"❌ 失败 | 原因: {error[:80]}")
fail += 1
time.sleep(0.3) # 避免请求过快,可自行调整
print("\n" + "=" * 50)
print(f"🎉 导入完成!新增成功: {success} 人,已存在: {exists} 人,失败: {fail} 人")
if __name__ == "__main__":
main()
📋 标准操作流程
1. 准备用户数据(CSV 文件)
创建一个 users.csv 文件,必须使用 UTF-8 编码(用 VS Code 或记事本另存为 UTF-8)。
列头严格如下:
username,displayName,email,password,description,role
示例数据:
210102071009,刘辉,stu@ycit.cn,Abc12345,期中成绩:24,
220102021022,李四,lisi@example.com,Pass5678,备注信息,role-template-viewer
字段说明:
username(必填) – 登录用户名/学号displayName(可选) – 显示名称(建议填写真实姓名,否则后台显示 username)email(可选,留空则自动生成username@default.com)password(必填) – 初始密码description(可选) – 个人简介(对应 Halo 中的bio字段)role(可选) – 角色名,如super-admin、role-template-editor等,留空则不分配角色
⚠️ 编码务必为 UTF-8,否则中文姓名会乱码。
2. 获取 Halo 个人令牌
- 登录 Halo 后台。
- 点击右上角头像 → 个人中心 → 个人令牌。
- 点击 创建令牌,填写名称,权限选择 全部,生成后复制 Token 值(格式为
pat_...)。
3. 配置脚本
用文本编辑器打开 ImpHalo.py,修改顶部两行:
HALO_URL = "https://你的域名"
API_TOKEN = "pat_你的令牌"
4. 运行脚本
在脚本所在目录打开终端,执行:
pip install requests # 若未安装
python3 ImpHalo.py
脚本会逐行显示导入结果:
- ✅ 成功 – 新增用户
- ℹ️ 已存在 – 用户名已存在,自动跳过
- ❌ 失败 – 显示具体错误原因
5. 验证结果
登录 Halo 后台 → 用户管理,检查:
- 用户列表是否显示正常的中文姓名(而非学号或乱码)
- 点击用户查看详情,个人简介是否正确填入
🧩 关键问题与解决方案回顾
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
404 No static resource | API 分组错误,使用了公开 API | 改为控制台 API:/apis/api.console.halo.run/v1alpha1/users |
Name is required | 请求体缺少 name 字段 | 添加 "name": username |
| 角色导入失败 | 字段应为 roles 数组,不是 role 字符串 | 改为 "roles": [角色名] 或空数组 |
| 失败但用户已创建 | 状态码为 200 被误判;重复导入导致 JSON 解析失败 | 同时接受 200/201 为成功,单独处理 409 |
| 姓名显示为学号或乱码 | CSV 非 UTF-8 编码;displayName 为空 | 保存为 UTF-8 编码,确保 displayName 列有汉字 |
| 个人简介为空 | Halo 字段名为 bio 不是 description | 请求体中改用 "bio": 内容 |
📌 重要注意事项
- 密码强度:Halo 默认要求密码至少 6 位,不能为纯数字等弱密码。
- 邮箱唯一性:多个用户使用同一邮箱可能导致后续操作异常,建议使用唯一邮箱。
- 角色名称:必须与后台已有的角色名完全一致,否则创建失败。
- 请求频率:脚本已加入 0.3 秒延时,可根据服务器情况调整。
按照上述流程,即可一次性完成 Halo 用户的批量导入,且所有字段正常显示。如有特殊需求(如批量更新、使用 Excel 自动填充等),可在本流程基础上扩展。