login_and_create_students.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 登录并批量创建学生信息的完整脚本
  5. 包含登录获取Token和批量创建学生两个功能
  6. """
  7. import requests
  8. import json
  9. import random
  10. import time
  11. from datetime import datetime
  12. import logging
  13. # 配置日志
  14. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  15. logger = logging.getLogger(__name__)
  16. class LoginAndCreateStudents:
  17. def __init__(self, base_url="http://localhost", api_prefix="/dev-api"):
  18. """
  19. 初始化登录和学生创建器
  20. Args:
  21. base_url: 基础URL
  22. api_prefix: API前缀
  23. """
  24. self.base_url = base_url
  25. self.api_prefix = api_prefix
  26. self.full_base_url = base_url + api_prefix
  27. self.token = None
  28. self.session = requests.Session()
  29. # 简单的数据生成器
  30. self.surnames = ['李', '王', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴']
  31. self.male_names = ['伟', '强', '磊', '军', '勇', '涛', '明', '超', '辉', '华']
  32. self.female_names = ['丽', '娜', '敏', '静', '佳', '雅', '莉', '萍', '红', '梅']
  33. self.provinces = ['北京市', '上海市', '广东省', '江苏省', '浙江省', '山东省', '河南省', '湖北省']
  34. def login(self, username="admin", password="admin123"):
  35. """
  36. 尝试登录获取Token
  37. """
  38. try:
  39. # 尝试标准登录
  40. login_data = {
  41. "tenantId": "000000",
  42. "username": username,
  43. "password": password,
  44. "clientId": "e5cd7e4891bf95d1d19206ce24a7b32e",
  45. "grantType": "password"
  46. }
  47. headers = {
  48. 'Content-Type': 'application/json;charset=utf-8',
  49. 'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
  50. 'referer': f'{self.base_url}/login?redirect=%252Findex',
  51. 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8'
  52. }
  53. logger.info("正在尝试登录...")
  54. response = self.session.post(
  55. f"{self.full_base_url}/auth/login",
  56. json=login_data,
  57. headers=headers,
  58. timeout=10
  59. )
  60. response.raise_for_status()
  61. result = response.json()
  62. logger.info(f"登录响应: {json.dumps(result, ensure_ascii=False, indent=2)}")
  63. if result.get('code') == 200 and result.get('data', {}).get('access_token'):
  64. self.token = result['data']['access_token']
  65. # 更新session的默认headers
  66. self.session.headers.update({
  67. 'Authorization': f'Bearer {self.token}',
  68. 'Content-Type': 'application/json'
  69. })
  70. logger.info(f"✅ 登录成功,Token: {self.token[:50]}...")
  71. return True
  72. else:
  73. logger.error(f"❌ 登录失败: {result.get('msg')}")
  74. return False
  75. except Exception as e:
  76. logger.error(f"❌ 登录异常: {str(e)}")
  77. return False
  78. def get_class_list(self):
  79. """获取班级列表"""
  80. if not self.token:
  81. logger.error("未登录,无法获取班级列表")
  82. return []
  83. try:
  84. url = f"{self.full_base_url}/classMsg/classMsg/list"
  85. params = {
  86. 'pageNum': 1,
  87. 'pageSize': 1000
  88. }
  89. response = self.session.get(url, params=params)
  90. response.raise_for_status()
  91. data = response.json()
  92. if data.get('code') == 200:
  93. classes = data.get('rows', [])
  94. logger.info(f"获取到 {len(classes)} 个班级")
  95. return classes
  96. else:
  97. logger.error(f"获取班级列表失败: {data.get('msg')}")
  98. return []
  99. except Exception as e:
  100. logger.error(f"获取班级列表异常: {str(e)}")
  101. return []
  102. def generate_name(self, gender):
  103. """生成姓名"""
  104. surname = random.choice(self.surnames)
  105. if gender == '0': # 男性
  106. given_name = random.choice(self.male_names)
  107. else: # 女性
  108. given_name = random.choice(self.female_names)
  109. return surname + given_name
  110. def generate_phone(self):
  111. """生成手机号"""
  112. prefixes = ['130', '131', '132', '133', '134', '135', '136', '137', '138', '139']
  113. prefix = random.choice(prefixes)
  114. suffix = ''.join([str(random.randint(0, 9)) for _ in range(8)])
  115. return prefix + suffix
  116. def generate_id_card(self, birth_date):
  117. """生成身份证号"""
  118. area_codes = ['110101', '110102', '310101', '310104', '440101', '440103']
  119. area_code = random.choice(area_codes)
  120. birth_str = birth_date.replace('-', '')
  121. sequence = str(random.randint(100, 999))
  122. check_digit = str(random.randint(0, 9))
  123. return area_code + birth_str + sequence + check_digit
  124. def generate_birth_date(self):
  125. """生成出生日期(18-22岁)"""
  126. today = datetime.now()
  127. birth_year = today.year - random.randint(18, 22)
  128. birth_month = random.randint(1, 12)
  129. birth_day = random.randint(1, 28)
  130. return f"{birth_year:04d}-{birth_month:02d}-{birth_day:02d}"
  131. def generate_student_data(self, class_info, student_index):
  132. """生成学生数据"""
  133. gender = random.choice(['0', '1'])
  134. name = self.generate_name(gender)
  135. # 生成学号
  136. class_code = class_info.get('classCode', class_info.get('class_code', ''))
  137. if not class_code:
  138. class_code = f"CLS{class_info.get('id', '')}"
  139. student_no = f"{class_code}{student_index:02d}"
  140. birth_date = self.generate_birth_date()
  141. student_data = {
  142. # 基础信息
  143. "studentNo": student_no,
  144. "idCard": self.generate_id_card(birth_date),
  145. "name": name,
  146. "gender": gender,
  147. "birthDate": birth_date,
  148. "nationality": random.choice(['01', '02', '03', '04', '05']),
  149. "politicalStatus": random.choice(['01', '02', '03', '13']),
  150. "phone": self.generate_phone(),
  151. "email": f"{student_no}@student.edu.cn",
  152. # 学籍信息
  153. "classId": class_info.get('id'),
  154. "className": class_info.get('className', class_info.get('class_name', '')),
  155. "majorId": class_info.get('majorId', class_info.get('major_id')),
  156. "majorName": class_info.get('majorName', class_info.get('major_name', '')),
  157. "collegeId": class_info.get('collegeId', class_info.get('college_id')),
  158. "collegeName": class_info.get('collegeName', class_info.get('college_name', '')),
  159. "grade": class_info.get('grade', ''),
  160. "enrollmentYear": class_info.get('enrollmentYear', class_info.get('enrollment_year')),
  161. "academicLevel": class_info.get('classType', class_info.get('class_type', 1)),
  162. "classTeacherId": class_info.get('classTeacherId', class_info.get('class_teacher_id')),
  163. "classTeacherName": class_info.get('classTeacherName', class_info.get('class_teacher_name', '')),
  164. "schoolSystem": class_info.get('schoolSystem', class_info.get('school_system', 3)),
  165. # 默认状态
  166. "studentStatus": "1",
  167. "trainingType": "全日制",
  168. "admissionCategory": "统招",
  169. "householdRegisterType": "非农业",
  170. "educationLevel": "2",
  171. "maritalStatus": "1",
  172. # 地址信息
  173. "nativePlace": random.choice(self.provinces),
  174. "sourceRegion": random.choice(self.provinces),
  175. "homeAddress": f"{random.choice(self.provinces)}某市某区某街道{random.randint(1, 999)}号",
  176. "currentAddress": "学校宿舍",
  177. # 家庭信息
  178. "fatherName": self.generate_name('0'),
  179. "fatherPhone": self.generate_phone(),
  180. "motherName": self.generate_name('1'),
  181. "motherPhone": self.generate_phone(),
  182. # 健康信息
  183. "height": random.randint(155, 185),
  184. "weight": random.randint(45, 80),
  185. "bloodType": random.choice(['A', 'B', 'AB', 'O']),
  186. "healthStatus": "良好",
  187. # 布尔字段
  188. "isBoarder": 1,
  189. "isPoorStudent": random.randint(0, 1),
  190. "isMinority": 0,
  191. "isDisabled": 0,
  192. "isOrphan": 0,
  193. "isLeftBehindChild": 0,
  194. "isSingleChild": random.randint(0, 1),
  195. "isLowIncome": 0,
  196. "hasLoan": random.randint(0, 1),
  197. "remark": f"批量生成的测试学生数据 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
  198. }
  199. return student_data
  200. def create_student(self, student_data):
  201. """创建单个学生"""
  202. try:
  203. url = f"{self.full_base_url}/studentMsg/studentMsg"
  204. response = self.session.post(url, json=student_data)
  205. response.raise_for_status()
  206. result = response.json()
  207. if result.get('code') == 200:
  208. logger.info(f"成功创建学生: {student_data['name']} ({student_data['studentNo']})")
  209. return True, result
  210. else:
  211. logger.error(f"创建学生失败: {student_data['name']} - {result.get('msg')}")
  212. return False, result
  213. except Exception as e:
  214. logger.error(f"创建学生异常: {student_data['name']} - {str(e)}")
  215. return False, str(e)
  216. def batch_create_students_for_class(self, class_info, student_count=30):
  217. """为指定班级批量创建学生"""
  218. class_name = class_info.get('className', class_info.get('class_name', f"班级{class_info.get('id')}"))
  219. logger.info(f"开始为班级 [{class_name}] 创建 {student_count} 个学生...")
  220. success_count = 0
  221. failed_count = 0
  222. failed_students = []
  223. for i in range(1, student_count + 1):
  224. try:
  225. student_data = self.generate_student_data(class_info, i)
  226. success, result = self.create_student(student_data)
  227. if success:
  228. success_count += 1
  229. else:
  230. failed_count += 1
  231. failed_students.append({
  232. 'student_no': student_data['studentNo'],
  233. 'name': student_data['name'],
  234. 'error': result
  235. })
  236. time.sleep(0.1) # 短暂延迟
  237. except Exception as e:
  238. failed_count += 1
  239. logger.error(f"处理第 {i} 个学生时发生异常: {str(e)}")
  240. result = {
  241. 'class_name': class_name,
  242. 'total': student_count,
  243. 'success': success_count,
  244. 'failed': failed_count,
  245. 'failed_students': failed_students
  246. }
  247. logger.info(f"班级 [{class_name}] 学生创建完成: 成功 {success_count},失败 {failed_count}")
  248. return result
  249. def run(self, username="admin", password="admin123", student_count_per_class=30):
  250. """运行完整流程"""
  251. print("🚀 开始执行批量创建学生流程...")
  252. # 1. 登录
  253. if not self.login(username, password):
  254. print("❌ 登录失败,无法继续")
  255. return
  256. # 2. 获取班级列表
  257. classes = self.get_class_list()
  258. if not classes:
  259. print("❌ 没有获取到班级信息")
  260. return
  261. print(f"\n📋 找到 {len(classes)} 个班级,每个班级将创建 {student_count_per_class} 个学生")
  262. # 显示班级信息
  263. for i, class_info in enumerate(classes[:5], 1):
  264. print(f" {i}. {class_info.get('className', class_info.get('class_name', '未知'))} "
  265. f"({class_info.get('collegeName', class_info.get('college_name', '未知'))})")
  266. if len(classes) > 5:
  267. print(f" ... 还有 {len(classes) - 5} 个班级")
  268. # 3. 确认操作
  269. confirm = input(f"\n确认为所有 {len(classes)} 个班级创建学生?(y/N): ").strip().lower()
  270. if confirm not in ['y', 'yes']:
  271. print("操作已取消")
  272. return
  273. # 4. 批量创建学生
  274. start_time = time.time()
  275. all_results = []
  276. total_success = 0
  277. total_failed = 0
  278. for i, class_info in enumerate(classes, 1):
  279. print(f"\n📚 处理第 {i}/{len(classes)} 个班级...")
  280. try:
  281. result = self.batch_create_students_for_class(class_info, student_count_per_class)
  282. all_results.append(result)
  283. total_success += result['success']
  284. total_failed += result['failed']
  285. time.sleep(0.5) # 班级间延迟
  286. except Exception as e:
  287. logger.error(f"处理班级时发生异常: {str(e)}")
  288. total_failed += student_count_per_class
  289. end_time = time.time()
  290. # 5. 输出结果
  291. print("\n" + "="*50)
  292. print("🎉 批量创建学生完成!")
  293. print(f"📊 总班级数: {len(classes)}")
  294. print(f"👥 预计学生数: {len(classes) * student_count_per_class}")
  295. print(f"✅ 成功创建: {total_success}")
  296. print(f"❌ 创建失败: {total_failed}")
  297. print(f"⏱️ 总耗时: {end_time - start_time:.2f} 秒")
  298. print("="*50)
  299. # 保存结果
  300. timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
  301. result_file = f"student_creation_result_{timestamp}.json"
  302. with open(result_file, 'w', encoding='utf-8') as f:
  303. json.dump(all_results, f, ensure_ascii=False, indent=2)
  304. print(f"📄 详细结果已保存到: {result_file}")
  305. def main():
  306. """主函数"""
  307. print("🏫 学生批量创建工具")
  308. print("="*50)
  309. # 配置参数
  310. base_url = input("请输入系统地址 (默认: http://localhost): ").strip()
  311. if not base_url:
  312. base_url = "http://localhost"
  313. username = input("请输入用户名 (默认: admin): ").strip()
  314. if not username:
  315. username = "admin"
  316. password = input("请输入密码 (默认: admin123): ").strip()
  317. if not password:
  318. password = "admin123"
  319. student_count = input("请输入每个班级创建的学生数量 (默认: 30): ").strip()
  320. if not student_count:
  321. student_count = 30
  322. else:
  323. try:
  324. student_count = int(student_count)
  325. except ValueError:
  326. print("输入的学生数量无效,使用默认值 30")
  327. student_count = 30
  328. # 创建并运行
  329. creator = LoginAndCreateStudents(base_url)
  330. creator.run(username, password, student_count)
  331. if __name__ == "__main__":
  332. main()