routes_users.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import { Hono } from 'hono'
  2. import type { Variables, WithAuth } from "./middlewares.ts";
  3. export function createUserRoutes(withAuth: WithAuth) {
  4. const usersRoutes = new Hono<{ Variables: Variables }>()
  5. // 获取用户列表
  6. usersRoutes.get('/', withAuth, async (c) => {
  7. try {
  8. const apiClient = c.get('apiClient')
  9. const page = Number(c.req.query('page')) || 1
  10. const pageSize = Number(c.req.query('pageSize')) || 10
  11. const offset = (page - 1) * pageSize
  12. const search = c.req.query('search') || ''
  13. let query = apiClient.database.table('users')
  14. .orderBy('id', 'desc')
  15. if (search) {
  16. query = query.where((builder) => {
  17. builder.where('username', 'like', `%${search}%`)
  18. .orWhere('nickname', 'like', `%${search}%`)
  19. .orWhere('email', 'like', `%${search}%`)
  20. })
  21. }
  22. const total = await query.clone().count()
  23. const users = await query.select('id', 'username', 'nickname', 'email', 'phone', 'created_at')
  24. .limit(pageSize).offset(offset)
  25. return c.json({
  26. data: users,
  27. pagination: {
  28. total: Number(total),
  29. current: page,
  30. pageSize
  31. }
  32. })
  33. } catch (error) {
  34. console.error('获取用户列表失败:', error)
  35. return c.json({ error: '获取用户列表失败' }, 500)
  36. }
  37. })
  38. // 获取单个用户详情
  39. usersRoutes.get('/:id', withAuth, async (c) => {
  40. try {
  41. const id = Number(c.req.param('id'))
  42. if (!id || isNaN(id)) {
  43. return c.json({ error: '无效的用户ID' }, 400)
  44. }
  45. const apiClient = c.get('apiClient')
  46. const user = await apiClient.database.table('users')
  47. .where('id', id)
  48. .select('id', 'username', 'nickname', 'email', 'phone', 'role', 'created_at')
  49. .first()
  50. if (!user) {
  51. return c.json({ error: '用户不存在' }, 404)
  52. }
  53. return c.json({
  54. data: user,
  55. message: '获取用户详情成功'
  56. })
  57. } catch (error) {
  58. console.error('获取用户详情失败:', error)
  59. return c.json({ error: '获取用户详情失败' }, 500)
  60. }
  61. })
  62. // 创建用户
  63. usersRoutes.post('/', withAuth, async (c) => {
  64. try {
  65. const apiClient = c.get('apiClient')
  66. const body = await c.req.json()
  67. // 验证必填字段
  68. const { username, nickname, email, password, role } = body
  69. if (!username || !nickname || !email || !password || !role) {
  70. return c.json({ error: '缺少必要的用户信息' }, 400)
  71. }
  72. // 检查用户名是否已存在
  73. const existingUser = await apiClient.database.table('users')
  74. .where('username', username)
  75. .first()
  76. if (existingUser) {
  77. return c.json({ error: '用户名已存在' }, 400)
  78. }
  79. // 创建用户
  80. const [id] = await apiClient.database.table('users').insert({
  81. username,
  82. nickname,
  83. email,
  84. password: password, // 加密密码
  85. role,
  86. created_at: new Date(),
  87. updated_at: new Date()
  88. })
  89. const newUser = await apiClient.database.table('users')
  90. .where('id', id)
  91. .select('id', 'username', 'nickname', 'email', 'role', 'created_at')
  92. .first()
  93. return c.json({
  94. data: newUser,
  95. message: '创建用户成功'
  96. })
  97. } catch (error) {
  98. console.error('创建用户失败:', error)
  99. return c.json({ error: '创建用户失败' }, 500)
  100. }
  101. })
  102. // 更新用户
  103. usersRoutes.put('/:id', withAuth, async (c) => {
  104. try {
  105. const id = Number(c.req.param('id'))
  106. if (!id || isNaN(id)) {
  107. return c.json({ error: '无效的用户ID' }, 400)
  108. }
  109. const apiClient = c.get('apiClient')
  110. const body = await c.req.json()
  111. // 验证必填字段
  112. const { username, nickname, email, role } = body
  113. if (!username || !nickname || !email || !role) {
  114. return c.json({ error: '缺少必要的用户信息' }, 400)
  115. }
  116. // 检查用户是否存在
  117. const existingUser = await apiClient.database.table('users')
  118. .where('id', id)
  119. .first()
  120. if (!existingUser) {
  121. return c.json({ error: '用户不存在' }, 404)
  122. }
  123. // 如果修改了用户名,检查新用户名是否已被使用
  124. if (username !== existingUser.username) {
  125. const userWithSameName = await apiClient.database.table('users')
  126. .where('username', username)
  127. .whereNot('id', id.toString())
  128. .first()
  129. if (userWithSameName) {
  130. return c.json({ error: '用户名已存在' }, 400)
  131. }
  132. }
  133. // 更新用户信息
  134. const updateData: any = {
  135. username,
  136. nickname,
  137. email,
  138. role,
  139. updated_at: new Date()
  140. }
  141. // 如果提供了新密码,则更新密码
  142. if (body.password) {
  143. updateData.password = body.password
  144. }
  145. await apiClient.database.table('users')
  146. .where('id', id)
  147. .update(updateData)
  148. const updatedUser = await apiClient.database.table('users')
  149. .where('id', id)
  150. .select('id', 'username', 'nickname', 'email', 'role', 'created_at')
  151. .first()
  152. return c.json({
  153. data: updatedUser,
  154. message: '更新用户成功'
  155. })
  156. } catch (error) {
  157. console.error('更新用户失败:', error)
  158. return c.json({ error: '更新用户失败' }, 500)
  159. }
  160. })
  161. // 删除用户
  162. usersRoutes.delete('/:id', withAuth, async (c) => {
  163. try {
  164. const id = Number(c.req.param('id'))
  165. if (!id || isNaN(id)) {
  166. return c.json({ error: '无效的用户ID' }, 400)
  167. }
  168. const apiClient = c.get('apiClient')
  169. // 检查用户是否存在
  170. const existingUser = await apiClient.database.table('users')
  171. .where('id', id)
  172. .first()
  173. if (!existingUser) {
  174. return c.json({ error: '用户不存在' }, 404)
  175. }
  176. // 检查是否为最后一个管理员
  177. if (existingUser.role === 'admin') {
  178. const adminCount = await apiClient.database.table('users')
  179. .where('role', 'admin')
  180. .count()
  181. if (Number(adminCount) <= 1) {
  182. return c.json({ error: '不能删除最后一个管理员' }, 400)
  183. }
  184. }
  185. // 删除用户
  186. await apiClient.database.table('users')
  187. .where('id', id)
  188. .delete()
  189. return c.json({
  190. message: '删除用户成功',
  191. id
  192. })
  193. } catch (error) {
  194. console.error('删除用户失败:', error)
  195. return c.json({ error: '删除用户失败' }, 500)
  196. }
  197. })
  198. // 获取当前用户信息
  199. usersRoutes.get('/me/profile', withAuth, async (c) => {
  200. try {
  201. const user = c.get('user')!
  202. const apiClient = c.get('apiClient')
  203. const userData = await apiClient.database.table('users')
  204. .where('id', user.id)
  205. .select('id', 'username', 'nickname', 'email', 'phone', 'created_at')
  206. .first()
  207. if (!user) {
  208. return c.json({ error: '用户不存在' }, 404)
  209. }
  210. return c.json({
  211. data: userData,
  212. message: '获取用户详情成功'
  213. })
  214. } catch (error) {
  215. console.error('获取当前用户信息失败:', error)
  216. return c.json({ error: '获取当前用户信息失败' }, 500)
  217. }
  218. })
  219. // 更新当前用户信息
  220. usersRoutes.put('/me/profile', withAuth, async (c) => {
  221. try {
  222. const user = c.get('user')!
  223. const apiClient = c.get('apiClient')
  224. const body = await c.req.json()
  225. // 验证必填字段
  226. const { nickname, email, phone } = body
  227. if (!nickname || !email) {
  228. return c.json({ error: '缺少必要的用户信息' }, 400)
  229. }
  230. // 更新用户信息
  231. const updateData: any = {
  232. nickname,
  233. email,
  234. phone: phone || null,
  235. updated_at: new Date()
  236. }
  237. // 如果提供了新密码,则更新密码
  238. if (body.password) {
  239. updateData.password = body.password
  240. }
  241. await apiClient.database.table('users')
  242. .where('id', user.id)
  243. .update(updateData)
  244. const updatedUser = await apiClient.database.table('users')
  245. .where('id', user.id)
  246. .select('id', 'username', 'nickname', 'email', 'phone', 'created_at')
  247. .first()
  248. return c.json({
  249. data: updatedUser,
  250. message: '更新用户信息成功'
  251. })
  252. } catch (error) {
  253. console.error('更新当前用户信息失败:', error)
  254. return c.json({ error: '更新当前用户信息失败' }, 500)
  255. }
  256. })
  257. /**
  258. * 将粉丝用户转换为学员并设置有效期
  259. * @param {number} id - 用户ID
  260. * @param {string} expiresAt - 有效期截止日期 (ISO格式)
  261. * @returns {object} 转换后的用户信息
  262. */
  263. usersRoutes.post('/:id/convert-to-student', withAuth, async (c) => {
  264. try {
  265. const user = c.get('user')!
  266. // 检查是否为管理员或老师
  267. if (user.role !== 'admin' && user.role !== 'teacher') {
  268. return c.json({ error: '无权执行此操作' }, 403)
  269. }
  270. const id = Number(c.req.param('id'))
  271. if (!id || isNaN(id)) {
  272. return c.json({ error: '无效的用户ID' }, 400)
  273. }
  274. const apiClient = c.get('apiClient')
  275. const body = await c.req.json()
  276. // 验证必填字段
  277. const { expiresAt } = body
  278. if (!expiresAt) {
  279. return c.json({ error: '缺少有效期参数' }, 400)
  280. }
  281. // 检查用户是否存在且为粉丝
  282. const existingUser = await apiClient.database.table('users')
  283. .where('id', id)
  284. .first()
  285. if (!existingUser) {
  286. return c.json({ error: '用户不存在' }, 404)
  287. }
  288. if (existingUser.role !== 'fan') {
  289. return c.json({ error: '只能将粉丝转为学员' }, 400)
  290. }
  291. // 更新用户角色和有效期
  292. await apiClient.database.table('users')
  293. .where('id', id)
  294. .update({
  295. role: 'student',
  296. student_expires_at: expiresAt,
  297. updated_at: new Date()
  298. })
  299. const updatedUser = await apiClient.database.table('users')
  300. .where('id', id)
  301. .select('id', 'username', 'nickname', 'role', 'student_expires_at')
  302. .first()
  303. return c.json({
  304. data: updatedUser,
  305. message: '用户已成功转为学员'
  306. })
  307. } catch (error) {
  308. console.error('转换用户角色失败:', error)
  309. return c.json({ error: '转换用户角色失败' }, 500)
  310. }
  311. })
  312. /**
  313. * 检查并转换过期学员
  314. * @returns {object} 转换结果统计
  315. */
  316. usersRoutes.get('/check-expired', withAuth, async (c) => {
  317. try {
  318. const apiClient = c.get('apiClient')
  319. const now = new Date().toISOString()
  320. // 查找所有已过期的学员
  321. const expiredStudents = await apiClient.database.table('users')
  322. .where('role', 'student')
  323. .where('student_expires_at', '<', now)
  324. .select('id', 'username', 'student_expires_at')
  325. if (expiredStudents.length === 0) {
  326. return c.json({
  327. data: {
  328. count: 0,
  329. users: []
  330. },
  331. message: '没有过期学员需要转换'
  332. })
  333. }
  334. // 批量转换过期学员为粉丝
  335. const userIds = expiredStudents.map(user => user.id)
  336. await apiClient.database.table('users')
  337. .whereIn('id', userIds)
  338. .update({
  339. role: 'follower',
  340. student_expires_at: null,
  341. updated_at: new Date()
  342. })
  343. return c.json({
  344. data: {
  345. count: expiredStudents.length,
  346. users: expiredStudents
  347. },
  348. message: '过期学员已成功转为粉丝'
  349. })
  350. } catch (error) {
  351. console.error('检查过期学员失败:', error)
  352. return c.json({ error: '检查过期学员失败' }, 500)
  353. }
  354. })
  355. return usersRoutes
  356. }