init_database.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. """
  2. 数据库表初始化脚本
  3. 运行: python init_database.py
  4. """
  5. import asyncio
  6. import aiomysql
  7. from config import settings
  8. import sys
  9. # 用户表
  10. CREATE_USERS_TABLE = """
  11. CREATE TABLE IF NOT EXISTS users (
  12. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  13. user_id VARCHAR(64) UNIQUE NOT NULL COMMENT '用户唯一标识(UUID)',
  14. -- 基本信息
  15. username VARCHAR(100) NOT NULL COMMENT '用户名/昵称',
  16. user_type ENUM('guest', 'registered') DEFAULT 'guest' COMMENT '用户类型',
  17. bot_name VARCHAR(100) COMMENT '用户给机器人取的名字',
  18. -- 注册信息(可选)
  19. phone VARCHAR(20) UNIQUE COMMENT '手机号',
  20. wechat_openid VARCHAR(100) UNIQUE COMMENT '微信OpenID',
  21. google_email VARCHAR(200) UNIQUE COMMENT 'Google邮箱',
  22. -- 认证信息
  23. is_verified BOOLEAN DEFAULT FALSE COMMENT '是否已认证',
  24. registered_at TIMESTAMP NULL COMMENT '注册时间',
  25. -- 统计信息
  26. total_messages INT DEFAULT 0 COMMENT '总消息数',
  27. total_rounds INT DEFAULT 0 COMMENT '总对话轮次',
  28. total_speech_duration_ms BIGINT DEFAULT 0 COMMENT '累积说话时长(毫秒)',
  29. last_active_at TIMESTAMP NULL COMMENT '最后活跃时间',
  30. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  31. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  32. INDEX idx_user_id (user_id),
  33. INDEX idx_phone (phone),
  34. INDEX idx_wechat_openid (wechat_openid),
  35. INDEX idx_google_email (google_email),
  36. INDEX idx_user_type (user_type)
  37. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  38. COMMENT='用户表'
  39. """
  40. # 消息表
  41. CREATE_MESSAGES_TABLE = """
  42. CREATE TABLE IF NOT EXISTS messages (
  43. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  44. message_id VARCHAR(64) UNIQUE NOT NULL COMMENT '消息ID',
  45. user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
  46. -- 消息内容
  47. role ENUM('user', 'assistant', 'system') NOT NULL COMMENT '角色',
  48. content TEXT NOT NULL COMMENT '消息内容',
  49. -- 轮次信息
  50. round_number INT NOT NULL COMMENT '对话轮次(从1开始)',
  51. -- 扩展信息
  52. emotion VARCHAR(50) COMMENT '情感标签',
  53. message_metadata JSON COMMENT '扩展信息(音频路径、识别耗时等)',
  54. token_count INT COMMENT 'Token数量',
  55. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  56. INDEX idx_user_id (user_id),
  57. INDEX idx_user_round (user_id, round_number),
  58. INDEX idx_created_at (created_at)
  59. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  60. COMMENT='消息表'
  61. """
  62. # 对话总结表
  63. CREATE_SUMMARIES_TABLE = """
  64. CREATE TABLE IF NOT EXISTS conversation_summaries (
  65. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  66. summary_id VARCHAR(64) UNIQUE NOT NULL COMMENT '总结ID',
  67. user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
  68. -- 总结范围
  69. start_round INT NOT NULL COMMENT '起始轮次',
  70. end_round INT NOT NULL COMMENT '结束轮次',
  71. -- 总结内容
  72. summary_text TEXT NOT NULL COMMENT '总结内容',
  73. summary_type ENUM('auto', 'manual') DEFAULT 'auto' COMMENT '总结类型',
  74. -- 统计信息
  75. message_count INT COMMENT '总结的消息数量',
  76. token_count INT COMMENT '总结文本Token数',
  77. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  78. INDEX idx_user_id (user_id),
  79. INDEX idx_user_end_round (user_id, end_round),
  80. UNIQUE KEY uk_user_round_range (user_id, start_round, end_round)
  81. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  82. COMMENT='对话总结表'
  83. """
  84. # LLM提示词记录表
  85. CREATE_LLM_PROMPTS_TABLE = """
  86. CREATE TABLE IF NOT EXISTS llm_prompts (
  87. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  88. prompt_id VARCHAR(64) UNIQUE NOT NULL COMMENT '提示词记录ID',
  89. -- 关联消息表(外键关联到messages.id)
  90. message_id BIGINT NOT NULL COMMENT '关联的消息ID',
  91. -- 用户信息
  92. user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
  93. round_number INT NOT NULL COMMENT '对话轮次',
  94. -- 提示词内容
  95. system_prompt TEXT NOT NULL COMMENT '系统提示词(完整内容)',
  96. runtime_context TEXT NULL COMMENT '运行时上下文',
  97. messages_json JSON NOT NULL COMMENT '完整的messages数组(JSON格式)',
  98. -- LLM调用参数
  99. model_name VARCHAR(100) NOT NULL COMMENT '模型名称',
  100. temperature VARCHAR(20) NULL COMMENT '温度参数',
  101. max_tokens INT NULL COMMENT '最大Token数',
  102. stream BOOLEAN DEFAULT TRUE COMMENT '是否流式输出',
  103. -- 统计信息
  104. prompt_token_count INT NULL COMMENT '提示词Token数',
  105. total_token_count INT NULL COMMENT '总Token数(包含响应)',
  106. -- 扩展信息
  107. metadata_json JSON NULL COMMENT '扩展信息(调用耗时、错误信息等)',
  108. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  109. INDEX idx_prompt_id (prompt_id),
  110. INDEX idx_llm_prompt_message (message_id),
  111. INDEX idx_llm_prompt_user_round (user_id, round_number),
  112. INDEX idx_llm_prompt_created (created_at),
  113. FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE
  114. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  115. COMMENT='LLM提示词记录表'
  116. """
  117. # 任务类型表
  118. CREATE_MISSION_TYPES_TABLE = """
  119. CREATE TABLE IF NOT EXISTS mission_types (
  120. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  121. type_key VARCHAR(64) UNIQUE NOT NULL COMMENT '任务类型唯一标识',
  122. display_name VARCHAR(100) NOT NULL COMMENT '任务显示名称',
  123. description VARCHAR(255) NULL COMMENT '类型描述',
  124. is_repeatable BOOLEAN DEFAULT FALSE COMMENT '是否可重复触发',
  125. is_active BOOLEAN DEFAULT TRUE COMMENT '是否启用',
  126. metadata JSON NULL COMMENT '扩展配置',
  127. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  128. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  129. INDEX idx_mission_type_active (is_active)
  130. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  131. COMMENT='任务类型配置表'
  132. """
  133. # 任务能量石表
  134. CREATE_MISSION_ENERGY_STONES_TABLE = """
  135. CREATE TABLE IF NOT EXISTS mission_energy_stones (
  136. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  137. stone_id VARCHAR(64) UNIQUE NOT NULL COMMENT '能量石ID',
  138. user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
  139. request_id VARCHAR(64) NULL COMMENT '触发的请求ID',
  140. appear_reason VARCHAR(64) NOT NULL DEFAULT 'first_dialog' COMMENT '触发原因',
  141. mission_type VARCHAR(64) NOT NULL DEFAULT 'first_dialog' COMMENT '任务类型',
  142. appear_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '出现时间',
  143. clicked_at TIMESTAMP NULL COMMENT '点击时间',
  144. auto_removed BOOLEAN DEFAULT FALSE COMMENT '是否自动消失',
  145. metadata JSON NULL COMMENT '扩展信息',
  146. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  147. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  148. INDEX idx_mission_stone_user (user_id, appear_at),
  149. INDEX idx_mission_type_user (mission_type, user_id)
  150. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  151. COMMENT='任务能量石表'
  152. """
  153. # 对话能量石表
  154. CREATE_CONVERSATION_ENERGY_STONES_TABLE = """
  155. CREATE TABLE IF NOT EXISTS conversation_energy_stones (
  156. id BIGINT PRIMARY KEY AUTO_INCREMENT,
  157. stone_id VARCHAR(64) UNIQUE NOT NULL COMMENT '能量石ID',
  158. user_id VARCHAR(64) NOT NULL COMMENT '用户ID',
  159. request_id VARCHAR(64) NULL COMMENT '触发的请求ID',
  160. threshold_index INT NOT NULL COMMENT '达成阈值的序号(1=30秒,2=60秒...)',
  161. total_duration_ms BIGINT NOT NULL COMMENT '累计说话时长(毫秒)',
  162. duration_increment_ms INT NOT NULL COMMENT '本次新增时长(毫秒)',
  163. appear_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '出现时间',
  164. clicked_at TIMESTAMP NULL COMMENT '点击时间',
  165. auto_removed BOOLEAN DEFAULT FALSE COMMENT '是否自动消失',
  166. metadata JSON NULL COMMENT '扩展信息',
  167. created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  168. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  169. UNIQUE KEY uk_conversation_stone_threshold (user_id, threshold_index),
  170. INDEX idx_conversation_stone_user_threshold (user_id, threshold_index)
  171. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  172. COMMENT='对话能量石表'
  173. """
  174. # 用户赠送对话能量石表(活动/回馈等,展示时与 conversation_energy_stones 数量相加)
  175. CREATE_USER_BONUS_ENERGY_STONES_TABLE = """
  176. CREATE TABLE IF NOT EXISTS user_bonus_energy_stones (
  177. user_id VARCHAR(64) PRIMARY KEY COMMENT '用户ID(关联 users.user_id)',
  178. bonus_conversation_count INT NOT NULL DEFAULT 0 COMMENT '赠送的对话能量石个数',
  179. updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
  180. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  181. COMMENT='用户赠送对话能量石数量表'
  182. """
  183. async def init_tables():
  184. """初始化数据库表"""
  185. try:
  186. # 创建数据库连接
  187. conn = await aiomysql.connect(
  188. host=settings.MYSQL_HOST,
  189. port=settings.MYSQL_PORT,
  190. user=settings.MYSQL_USER,
  191. password=settings.MYSQL_PASSWORD,
  192. db=settings.MYSQL_DATABASE,
  193. charset='utf8mb4'
  194. )
  195. async with conn.cursor() as cursor:
  196. # 创建用户表
  197. await cursor.execute(CREATE_USERS_TABLE)
  198. # 创建消息表
  199. await cursor.execute(CREATE_MESSAGES_TABLE)
  200. # 创建总结表
  201. await cursor.execute(CREATE_SUMMARIES_TABLE)
  202. # 创建LLM提示词记录表
  203. await cursor.execute(CREATE_LLM_PROMPTS_TABLE)
  204. # 创建任务类型表
  205. await cursor.execute(CREATE_MISSION_TYPES_TABLE)
  206. # 创建任务能量石表
  207. await cursor.execute(CREATE_MISSION_ENERGY_STONES_TABLE)
  208. # 创建对话能量石表
  209. await cursor.execute(CREATE_CONVERSATION_ENERGY_STONES_TABLE)
  210. # 创建用户赠送对话能量石表
  211. await cursor.execute(CREATE_USER_BONUS_ENERGY_STONES_TABLE)
  212. # 写入默认任务类型
  213. await cursor.execute(
  214. """
  215. INSERT INTO mission_types (type_key, display_name, description, is_repeatable)
  216. VALUES (%s, %s, %s, %s)
  217. ON DUPLICATE KEY UPDATE
  218. display_name = VALUES(display_name),
  219. description = VALUES(description),
  220. is_repeatable = VALUES(is_repeatable)
  221. """,
  222. (
  223. "first_dialog",
  224. "首次对话奖励",
  225. "首次进入对话时发放的任务能量石",
  226. False,
  227. ),
  228. )
  229. await conn.commit()
  230. conn.close()
  231. return True
  232. except Exception as e:
  233. import traceback
  234. traceback.print_exc()
  235. return False
  236. async def check_tables():
  237. """检查表是否存在"""
  238. try:
  239. conn = await aiomysql.connect(
  240. host=settings.MYSQL_HOST,
  241. port=settings.MYSQL_PORT,
  242. user=settings.MYSQL_USER,
  243. password=settings.MYSQL_PASSWORD,
  244. db=settings.MYSQL_DATABASE,
  245. charset='utf8mb4'
  246. )
  247. async with conn.cursor() as cursor:
  248. await cursor.execute("SHOW TABLES")
  249. tables = await cursor.fetchall()
  250. conn.close()
  251. except Exception as e:
  252. pass
  253. if __name__ == "__main__":
  254. # 确认是否继续
  255. if len(sys.argv) > 1 and sys.argv[1] == "--force":
  256. confirm = "y"
  257. else:
  258. confirm = input("\n是否继续初始化数据库表? (y/n): ")
  259. if confirm.lower() != 'y':
  260. sys.exit(0)
  261. # 执行初始化
  262. success = asyncio.run(init_tables())
  263. if success:
  264. # 检查创建的表
  265. asyncio.run(check_tables())
  266. else:
  267. sys.exit(1)