batch_create_students.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. 批量创建学生信息脚本
  5. 为每个班级创建30个学生的测试数据
  6. """
  7. import requests
  8. import json
  9. import random
  10. import time
  11. from datetime import datetime, timedelta
  12. import logging
  13. # 配置日志
  14. logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
  15. logger = logging.getLogger(__name__)
  16. class SimpleDataGenerator:
  17. """简单的数据生成器,替代faker"""
  18. def __init__(self):
  19. # 姓氏
  20. self.surnames = ['李', '王', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴',
  21. '徐', '孙', '朱', '马', '胡', '郭', '林', '何', '高', '梁']
  22. # 男性名字
  23. self.male_names = ['伟', '强', '磊', '军', '勇', '涛', '明', '超', '辉', '华',
  24. '建', '文', '健', '云', '峰', '浩', '鹏', '飞', '宇', '杰']
  25. # 女性名字
  26. self.female_names = ['丽', '娜', '敏', '静', '佳', '雅', '莉', '萍', '红', '梅',
  27. '芳', '燕', '玲', '霞', '琳', '晶', '欣', '宁', '蕾', '洁']
  28. # 省份
  29. self.provinces = ['北京市', '上海市', '天津市', '重庆市', '河北省', '山西省', '辽宁省',
  30. '吉林省', '黑龙江省', '江苏省', '浙江省', '安徽省', '福建省', '江西省',
  31. '山东省', '河南省', '湖北省', '湖南省', '广东省', '海南省', '四川省',
  32. '贵州省', '云南省', '陕西省', '甘肃省', '青海省', '台湾省', '内蒙古自治区',
  33. '广西壮族自治区', '西藏自治区', '宁夏回族自治区', '新疆维吾尔自治区']
  34. def generate_name(self, gender):
  35. """生成姓名"""
  36. surname = random.choice(self.surnames)
  37. if gender == '0': # 男性
  38. given_name = random.choice(self.male_names)
  39. else: # 女性
  40. given_name = random.choice(self.female_names)
  41. # 有时候生成双字名
  42. if random.random() < 0.3:
  43. if gender == '0':
  44. given_name += random.choice(self.male_names)
  45. else:
  46. given_name += random.choice(self.female_names)
  47. return surname + given_name
  48. def generate_phone(self):
  49. """生成手机号"""
  50. prefixes = ['130', '131', '132', '133', '134', '135', '136', '137', '138', '139',
  51. '150', '151', '152', '153', '155', '156', '157', '158', '159',
  52. '180', '181', '182', '183', '184', '185', '186', '187', '188', '189']
  53. prefix = random.choice(prefixes)
  54. suffix = ''.join([str(random.randint(0, 9)) for _ in range(8)])
  55. return prefix + suffix
  56. def generate_id_card(self, birth_date):
  57. """生成身份证号"""
  58. # 地区代码(简化版)
  59. area_codes = ['110101', '110102', '110105', '110106', '110107', '110108',
  60. '310101', '310104', '310105', '310106', '310107', '310109',
  61. '440101', '440103', '440104', '440105', '440106', '440111']
  62. area_code = random.choice(area_codes)
  63. # 出生日期(YYYYMMDD格式)
  64. birth_str = birth_date.replace('-', '')
  65. # 顺序码(3位)+ 校验码(1位)
  66. sequence = str(random.randint(100, 999))
  67. check_digit = str(random.randint(0, 9))
  68. return area_code + birth_str + sequence + check_digit
  69. def generate_address(self):
  70. """生成地址"""
  71. province = random.choice(self.provinces)
  72. cities = ['市', '县']
  73. city = f"某{random.choice(cities)}"
  74. district = f"某区某街道{random.randint(1, 999)}号"
  75. return province + city + district
  76. def generate_province(self):
  77. """生成省份"""
  78. return random.choice(self.provinces)
  79. class StudentBatchCreator:
  80. def __init__(self, base_url="http://localhost:8080", token=None):
  81. """
  82. 初始化学生批量创建器
  83. Args:
  84. base_url: 后端API基础URL
  85. token: 认证token
  86. """
  87. self.base_url = base_url
  88. self.token = token
  89. self.session = requests.Session()
  90. self.data_gen = SimpleDataGenerator()
  91. # 设置请求头
  92. if token:
  93. self.session.headers.update({
  94. 'Authorization': f'Bearer {token}',
  95. 'Content-Type': 'application/json'
  96. })
  97. def set_token(self, token):
  98. """设置认证token"""
  99. self.token = token
  100. self.session.headers.update({
  101. 'Authorization': f'Bearer {token}'
  102. })
  103. def get_class_list(self):
  104. """获取班级列表"""
  105. try:
  106. url = f"{self.base_url}/classMsg/classMsg/list"
  107. params = {
  108. 'pageNum': 1,
  109. 'pageSize': 1000 # 获取所有班级
  110. }
  111. response = self.session.get(url, params=params)
  112. response.raise_for_status()
  113. data = response.json()
  114. if data.get('code') == 200:
  115. classes = data.get('rows', [])
  116. logger.info(f"获取到 {len(classes)} 个班级")
  117. return classes
  118. else:
  119. logger.error(f"获取班级列表失败: {data.get('msg')}")
  120. return []
  121. except Exception as e:
  122. logger.error(f"获取班级列表异常: {str(e)}")
  123. return []
  124. def generate_birth_date(self):
  125. """生成出生日期(18-22岁)"""
  126. today = datetime.now()
  127. min_age = 18
  128. max_age = 22
  129. # 计算出生年份范围
  130. birth_year = today.year - random.randint(min_age, max_age)
  131. birth_month = random.randint(1, 12)
  132. birth_day = random.randint(1, 28) # 使用28避免月份天数问题
  133. return f"{birth_year:04d}-{birth_month:02d}-{birth_day:02d}"
  134. def generate_student_data(self, class_info, student_index):
  135. """
  136. 生成单个学生的测试数据
  137. Args:
  138. class_info: 班级信息
  139. student_index: 学生编号(1-30)
  140. Returns:
  141. dict: 学生数据
  142. """
  143. # 生成基础信息
  144. gender = random.choice(['0', '1']) # 0-男,1-女
  145. name = self.data_gen.generate_name(gender)
  146. # 生成学号:班级代码 + 学生编号
  147. class_code = class_info.get('classCode', class_info.get('class_code', ''))
  148. if not class_code:
  149. # 如果没有班级代码,使用班级ID
  150. class_code = f"CLS{class_info.get('id', '')}"
  151. student_no = f"{class_code}{student_index:02d}"
  152. # 生成出生日期
  153. birth_date = self.generate_birth_date()
  154. # 生成身份证号
  155. id_card = self.data_gen.generate_id_card(birth_date)
  156. # 生成手机号
  157. phone = self.data_gen.generate_phone()
  158. # 民族选择
  159. nations = ['01', '02', '03', '04', '05'] # 汉族、蒙古族、回族、藏族、维吾尔族等
  160. nationality = random.choice(nations)
  161. # 政治面貌选择
  162. political_statuses = ['01', '02', '03', '13'] # 群众、团员、党员等
  163. political_status = random.choice(political_statuses)
  164. # 构建学生数据
  165. student_data = {
  166. # 基础信息
  167. "studentNo": student_no,
  168. "idCard": id_card,
  169. "name": name,
  170. "gender": gender,
  171. "birthDate": birth_date,
  172. "nationality": nationality,
  173. "politicalStatus": political_status,
  174. "phone": phone,
  175. "email": f"{student_no}@student.edu.cn",
  176. # 学籍信息(从班级信息继承)
  177. "classId": class_info.get('id'),
  178. "className": class_info.get('className', class_info.get('class_name', '')),
  179. "majorId": class_info.get('majorId', class_info.get('major_id')),
  180. "majorName": class_info.get('majorName', class_info.get('major_name', '')),
  181. "collegeId": class_info.get('collegeId', class_info.get('college_id')),
  182. "collegeName": class_info.get('collegeName', class_info.get('college_name', '')),
  183. "grade": class_info.get('grade', ''),
  184. "enrollmentYear": class_info.get('enrollmentYear', class_info.get('enrollment_year')),
  185. "academicLevel": class_info.get('classType', class_info.get('class_type', 1)),
  186. "classTeacherId": class_info.get('classTeacherId', class_info.get('class_teacher_id')),
  187. "classTeacherName": class_info.get('classTeacherName', class_info.get('class_teacher_name', '')),
  188. "schoolSystem": class_info.get('schoolSystem', class_info.get('school_system', 3)),
  189. # 默认状态
  190. "studentStatus": "1", # 在籍
  191. "trainingType": "全日制",
  192. "admissionCategory": "统招",
  193. "householdRegisterType": "非农业",
  194. "educationLevel": "2", # 高中
  195. "maritalStatus": "1", # 未婚
  196. # 地址信息
  197. "nativePlace": self.data_gen.generate_province(),
  198. "sourceRegion": self.data_gen.generate_province(),
  199. "homeAddress": self.data_gen.generate_address(),
  200. "currentAddress": "学校宿舍",
  201. # 家庭信息
  202. "fatherName": self.data_gen.generate_name('0'),
  203. "fatherPhone": self.data_gen.generate_phone(),
  204. "motherName": self.data_gen.generate_name('1'),
  205. "motherPhone": self.data_gen.generate_phone(),
  206. # 健康信息
  207. "height": random.randint(155, 185),
  208. "weight": random.randint(45, 80),
  209. "bloodType": random.choice(['A', 'B', 'AB', 'O']),
  210. "healthStatus": "良好",
  211. # 布尔字段
  212. "isBoarder": 1, # 住校
  213. "isPoorStudent": random.randint(0, 1),
  214. "isMinority": 1 if nationality != '01' else 0, # 非汉族为少数民族
  215. "isDisabled": 0,
  216. "isOrphan": 0,
  217. "isLeftBehindChild": 0,
  218. "isSingleChild": random.randint(0, 1),
  219. "isLowIncome": 0,
  220. "hasLoan": random.randint(0, 1),
  221. # 其他字段
  222. "remark": f"批量生成的测试学生数据 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
  223. }
  224. return student_data
  225. def create_student(self, student_data):
  226. """
  227. 创建单个学生
  228. Args:
  229. student_data: 学生数据
  230. Returns:
  231. tuple: (success, result)
  232. """
  233. try:
  234. url = f"{self.base_url}/studentMsg/studentMsg"
  235. response = self.session.post(url, json=student_data)
  236. response.raise_for_status()
  237. result = response.json()
  238. if result.get('code') == 200:
  239. logger.info(f"成功创建学生: {student_data['name']} ({student_data['studentNo']})")
  240. return True, result
  241. else:
  242. logger.error(f"创建学生失败: {student_data['name']} - {result.get('msg')}")
  243. return False, result
  244. except Exception as e:
  245. logger.error(f"创建学生异常: {student_data['name']} - {str(e)}")
  246. return False, str(e)
  247. def batch_create_students_for_class(self, class_info, student_count=30):
  248. """
  249. 为指定班级批量创建学生
  250. Args:
  251. class_info: 班级信息
  252. student_count: 要创建的学生数量
  253. Returns:
  254. dict: 创建结果统计
  255. """
  256. class_name = class_info.get('className', class_info.get('class_name', f"班级{class_info.get('id')}"))
  257. logger.info(f"开始为班级 [{class_name}] 创建 {student_count} 个学生...")
  258. success_count = 0
  259. failed_count = 0
  260. failed_students = []
  261. for i in range(1, student_count + 1):
  262. try:
  263. # 生成学生数据
  264. student_data = self.generate_student_data(class_info, i)
  265. # 创建学生
  266. success, result = self.create_student(student_data)
  267. if success:
  268. success_count += 1
  269. else:
  270. failed_count += 1
  271. failed_students.append({
  272. 'student_no': student_data['studentNo'],
  273. 'name': student_data['name'],
  274. 'error': result
  275. })
  276. # 添加短暂延迟,避免请求过于频繁
  277. time.sleep(0.1)
  278. except Exception as e:
  279. failed_count += 1
  280. logger.error(f"处理第 {i} 个学生时发生异常: {str(e)}")
  281. failed_students.append({
  282. 'student_no': f"未知_{i}",
  283. 'name': '未知',
  284. 'error': str(e)
  285. })
  286. result = {
  287. 'class_name': class_name,
  288. 'total': student_count,
  289. 'success': success_count,
  290. 'failed': failed_count,
  291. 'failed_students': failed_students
  292. }
  293. logger.info(f"班级 [{class_name}] 学生创建完成: 成功 {success_count},失败 {failed_count}")
  294. return result
  295. def batch_create_students_for_all_classes(self, student_count_per_class=30):
  296. """
  297. 为所有班级批量创建学生
  298. Args:
  299. student_count_per_class: 每个班级创建的学生数量
  300. Returns:
  301. list: 所有班级的创建结果
  302. """
  303. # 获取班级列表
  304. classes = self.get_class_list()
  305. if not classes:
  306. logger.error("没有获取到班级信息,无法创建学生")
  307. return []
  308. logger.info(f"开始为 {len(classes)} 个班级批量创建学生,每个班级 {student_count_per_class} 个学生")
  309. all_results = []
  310. total_success = 0
  311. total_failed = 0
  312. for class_info in classes:
  313. try:
  314. result = self.batch_create_students_for_class(class_info, student_count_per_class)
  315. all_results.append(result)
  316. total_success += result['success']
  317. total_failed += result['failed']
  318. # 班级间添加延迟
  319. time.sleep(0.5)
  320. except Exception as e:
  321. logger.error(f"处理班级 {class_info} 时发生异常: {str(e)}")
  322. all_results.append({
  323. 'class_name': f"班级{class_info.get('id', '未知')}",
  324. 'total': student_count_per_class,
  325. 'success': 0,
  326. 'failed': student_count_per_class,
  327. 'error': str(e)
  328. })
  329. total_failed += student_count_per_class
  330. # 输出总结
  331. logger.info("="*50)
  332. logger.info("批量创建学生完成!")
  333. logger.info(f"总班级数: {len(classes)}")
  334. logger.info(f"总学生数: {len(classes) * student_count_per_class}")
  335. logger.info(f"成功创建: {total_success}")
  336. logger.info(f"创建失败: {total_failed}")
  337. logger.info("="*50)
  338. return all_results
  339. def main():
  340. """主函数"""
  341. print("学生批量创建工具")
  342. print("="*50)
  343. # 配置参数
  344. base_url = input("请输入后端API地址 (默认: http://localhost:8080): ").strip()
  345. if not base_url:
  346. base_url = "http://localhost:8080"
  347. token = input("请输入认证Token (可为空): ").strip()
  348. student_count = input("请输入每个班级创建的学生数量 (默认: 30): ").strip()
  349. if not student_count:
  350. student_count = 30
  351. else:
  352. try:
  353. student_count = int(student_count)
  354. except ValueError:
  355. print("输入的学生数量无效,使用默认值 30")
  356. student_count = 30
  357. # 创建批量创建器
  358. creator = StudentBatchCreator(base_url, token)
  359. # 确认操作
  360. print(f"\n配置信息:")
  361. print(f"API地址: {base_url}")
  362. print(f"每个班级学生数: {student_count}")
  363. confirm = input("\n确认开始批量创建学生?(y/N): ").strip().lower()
  364. if confirm not in ['y', 'yes']:
  365. print("操作已取消")
  366. return
  367. # 开始批量创建
  368. start_time = time.time()
  369. results = creator.batch_create_students_for_all_classes(student_count)
  370. end_time = time.time()
  371. # 保存结果到文件
  372. timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
  373. result_file = f"student_creation_result_{timestamp}.json"
  374. with open(result_file, 'w', encoding='utf-8') as f:
  375. json.dump(results, f, ensure_ascii=False, indent=2)
  376. print(f"\n创建结果已保存到: {result_file}")
  377. print(f"总耗时: {end_time - start_time:.2f} 秒")
  378. if __name__ == "__main__":
  379. main()