member.controller.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
  2. import { MemberService } from '../services/member.service'
  3. import {
  4. CreateMemberBody,
  5. UpdateMemberBody,
  6. ListMemberQuery,
  7. MemberResponse,
  8. UpdateGuestBody,
  9. MemberLoginBody,
  10. ResetPasswordBody
  11. } from '../dto/member.dto'
  12. import { VipLevel, MemberStatus } from '../entities/member.entity'
  13. import { UserService } from '../services/user.service'
  14. export class MemberController {
  15. private memberService: MemberService
  16. private userService: UserService
  17. constructor(app: FastifyInstance) {
  18. this.memberService = new MemberService(app)
  19. this.userService = new UserService(app)
  20. }
  21. async createGuest(request: FastifyRequest<{ Querystring: { code?: string } }>, reply: FastifyReply) {
  22. try {
  23. const { code } = request.query || {}
  24. const ip =
  25. request.ip ||
  26. (request.headers['x-forwarded-for'] as string) ||
  27. (request.headers['x-real-ip'] as string) ||
  28. 'unknown'
  29. const user = await this.memberService.createGuest(code, ip)
  30. const token = await reply.jwtSign({ id: user.id, name: user.name, role: user.role })
  31. return reply.code(201).send({
  32. user: {
  33. id: user.id,
  34. name: user.name,
  35. vipLevel: VipLevel.GUEST
  36. },
  37. token
  38. })
  39. } catch (error) {
  40. return reply.code(500).send({ message: '创建游客失败', error })
  41. }
  42. }
  43. async upgradeGuest(request: FastifyRequest<{ Body: UpdateGuestBody }>, reply: FastifyReply) {
  44. try {
  45. const { userId, name, password, email, phone } = request.body
  46. if (!name || !password) {
  47. return reply.code(400).send({ message: '用户名和密码为必填字段' })
  48. }
  49. if (name.length < 3 || name.length > 20) {
  50. return reply.code(400).send({ message: '用户名长度必须在3-20个字符之间' })
  51. }
  52. if (password.length < 6) {
  53. return reply.code(400).send({ message: '密码长度不能少于6个字符' })
  54. }
  55. if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
  56. return reply.code(400).send({ message: '邮箱格式不正确' })
  57. }
  58. if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
  59. return reply.code(400).send({ message: '手机号格式不正确' })
  60. }
  61. await this.memberService.upgradeGuest(userId, name, password, email, phone)
  62. return reply.send({ message: '游客账户转换成功' })
  63. } catch (error) {
  64. const errorMessage = error instanceof Error ? error.message : '更新游客失败'
  65. if (errorMessage.includes('不存在')) {
  66. return reply.code(404).send({ message: errorMessage })
  67. } else if (errorMessage.includes('已被使用') || errorMessage.includes('格式')) {
  68. return reply.code(400).send({ message: errorMessage })
  69. }
  70. return reply.code(500).send({ message: '更新游客失败', error: errorMessage })
  71. }
  72. }
  73. async memberLogin(request: FastifyRequest<{ Body: MemberLoginBody }>, reply: FastifyReply) {
  74. try {
  75. const { name, password } = request.body
  76. if (!name || !password) {
  77. return reply.code(400).send({ message: '请输入用户名和密码' })
  78. }
  79. const loginResult = await this.memberService.validateMemberLogin(name, password)
  80. if (!loginResult) {
  81. return reply.code(401).send({ message: '用户名或密码错误' })
  82. }
  83. const { user, member } = loginResult
  84. const token = await reply.jwtSign({ id: user.id, name: user.name, role: user.role })
  85. await this.memberService.checkVipExpireTime(member)
  86. return reply.send({
  87. user: {
  88. id: user.id,
  89. name: user.name,
  90. role: user.role,
  91. vipLevel: member.vipLevel
  92. },
  93. token
  94. })
  95. } catch (error) {
  96. return reply.code(500).send(error)
  97. }
  98. }
  99. async profile(request: FastifyRequest, reply: FastifyReply) {
  100. try {
  101. const user = await this.userService.findById(request.user.id)
  102. if (!user) {
  103. return reply.code(404).send({ message: '会员信息不存在' })
  104. }
  105. const member = await this.memberService.findByUserId(user.id)
  106. if (!member) {
  107. return reply.code(404).send({ message: '会员信息不存在' })
  108. }
  109. await this.memberService.checkVipExpireTime(member)
  110. return reply.send({
  111. id: user.id,
  112. name: user.name,
  113. role: user.role,
  114. vipLevel: member.vipLevel,
  115. vipExpireTime: member.vipExpireTime
  116. })
  117. } catch (error) {
  118. return reply.code(500).send(error)
  119. }
  120. }
  121. async resetPassword(request: FastifyRequest<{ Body: ResetPasswordBody }>, reply: FastifyReply) {
  122. try {
  123. const { password } = request.body
  124. if (password.length < 8 || !/(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/.test(password)) {
  125. return reply.code(400).send({ message: '密码长度必须至少8位,包含大小写字母和数字' })
  126. }
  127. await this.userService.resetPassword(request.user.id, password)
  128. return reply.send({ message: '密码重置成功' })
  129. } catch (error) {
  130. return reply.code(500).send(error)
  131. }
  132. }
  133. async updateVipLevel(
  134. request: FastifyRequest<{
  135. Params: { id: string }
  136. Body: { vipLevel: VipLevel; vipExpireTime?: Date }
  137. }>,
  138. reply: FastifyReply
  139. ) {
  140. try {
  141. const id = parseInt(request.params.id)
  142. const { vipLevel, vipExpireTime } = request.body
  143. const updatedMember = await this.memberService.updateVipLevel(id, vipLevel, vipExpireTime)
  144. return reply.send({
  145. member: {
  146. vipLevel: updatedMember.vipLevel,
  147. vipExpireTime: updatedMember.vipExpireTime
  148. }
  149. })
  150. } catch (error) {
  151. return reply.code(500).send({ message: '更新VIP等级失败', error })
  152. }
  153. }
  154. async create(request: FastifyRequest<{ Body: CreateMemberBody }>, reply: FastifyReply) {
  155. try {
  156. const { userId, email, phone, vipLevel, status, vipExpireTime } = request.body
  157. // 检查用户是否已存在
  158. const existingMember = await this.memberService.findByUserId(userId)
  159. if (existingMember) {
  160. return reply.code(400).send({ message: '该用户已经是会员' })
  161. }
  162. // 检查邮箱是否已存在
  163. if (email) {
  164. const existingEmail = await this.memberService.findByEmail(email)
  165. if (existingEmail) {
  166. return reply.code(400).send({ message: '邮箱已被使用' })
  167. }
  168. }
  169. // 检查手机号是否已存在
  170. if (phone) {
  171. const existingPhone = await this.memberService.findByPhone(phone)
  172. if (existingPhone) {
  173. return reply.code(400).send({ message: '手机号已被使用' })
  174. }
  175. }
  176. const member = await this.memberService.create({
  177. userId,
  178. email,
  179. phone,
  180. vipLevel,
  181. status,
  182. vipExpireTime
  183. })
  184. return reply.code(201).send({ message: '创建会员成功' })
  185. } catch (error) {
  186. return reply.code(500).send({ message: '创建会员失败', error })
  187. }
  188. }
  189. async getById(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
  190. try {
  191. const id = parseInt(request.params.id)
  192. const member = await this.memberService.findById(id)
  193. return reply.send({
  194. member: {
  195. id: member.id,
  196. userId: member.userId,
  197. email: member.email,
  198. phone: member.phone,
  199. vipLevel: member.vipLevel,
  200. status: member.status,
  201. vipExpireTime: member.vipExpireTime,
  202. lastLoginAt: member.lastLoginAt,
  203. createdAt: member.createdAt,
  204. updatedAt: member.updatedAt
  205. }
  206. })
  207. } catch (error) {
  208. return reply.code(404).send({ message: '会员不存在' })
  209. }
  210. }
  211. async getByUserId(request: FastifyRequest<{ Params: { userId: string } }>, reply: FastifyReply) {
  212. try {
  213. const userId = parseInt(request.params.userId)
  214. const member = await this.memberService.findByUserId(userId)
  215. if (!member) {
  216. return reply.code(404).send({ message: '会员不存在' })
  217. }
  218. return reply.send({
  219. member: {
  220. id: member.id,
  221. userId: member.userId,
  222. email: member.email,
  223. phone: member.phone,
  224. vipLevel: member.vipLevel,
  225. status: member.status,
  226. vipExpireTime: member.vipExpireTime,
  227. lastLoginAt: member.lastLoginAt,
  228. createdAt: member.createdAt,
  229. updatedAt: member.updatedAt
  230. }
  231. })
  232. } catch (error) {
  233. return reply.code(500).send({ message: '查询会员失败', error })
  234. }
  235. }
  236. async list(request: FastifyRequest<{ Querystring: ListMemberQuery }>, reply: FastifyReply) {
  237. try {
  238. const { page, size, vipLevel, status, userId } = request.query
  239. const result = await this.memberService.list(page || 0, size || 20, { vipLevel, status, userId })
  240. return reply.send(result)
  241. } catch (error) {
  242. return reply.code(500).send({ message: '查询会员列表失败', error })
  243. }
  244. }
  245. async update(request: FastifyRequest<{ Body: UpdateMemberBody }>, reply: FastifyReply) {
  246. try {
  247. const { id, userId, email, phone, vipLevel, status, vipExpireTime } = request.body
  248. // 检查会员是否存在
  249. try {
  250. await this.memberService.findById(id)
  251. } catch (error) {
  252. return reply.code(404).send({ message: '会员不存在' })
  253. }
  254. // 检查邮箱是否已被其他会员使用
  255. if (email) {
  256. const existingEmail = await this.memberService.findByEmail(email)
  257. if (existingEmail && existingEmail.id !== id) {
  258. return reply.code(400).send({ message: '邮箱已被其他会员使用' })
  259. }
  260. }
  261. // 检查手机号是否已被其他会员使用
  262. if (phone) {
  263. const existingPhone = await this.memberService.findByPhone(phone)
  264. if (existingPhone && existingPhone.id !== id) {
  265. return reply.code(400).send({ message: '手机号已被其他会员使用' })
  266. }
  267. }
  268. const updatedMember = await this.memberService.update(id, {
  269. userId,
  270. email,
  271. phone,
  272. vipLevel,
  273. status,
  274. vipExpireTime
  275. })
  276. return reply.send({
  277. member: {
  278. id: updatedMember.id,
  279. userId: updatedMember.userId,
  280. email: updatedMember.email,
  281. phone: updatedMember.phone,
  282. vipLevel: updatedMember.vipLevel,
  283. status: updatedMember.status,
  284. vipExpireTime: updatedMember.vipExpireTime,
  285. lastLoginAt: updatedMember.lastLoginAt,
  286. createdAt: updatedMember.createdAt,
  287. updatedAt: updatedMember.updatedAt
  288. }
  289. })
  290. } catch (error) {
  291. return reply.code(500).send({ message: '更新会员失败', error })
  292. }
  293. }
  294. async delete(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
  295. try {
  296. const id = parseInt(request.params.id)
  297. // 检查会员是否存在
  298. try {
  299. await this.memberService.findById(id)
  300. } catch (error) {
  301. return reply.code(404).send({ message: '会员不存在' })
  302. }
  303. await this.memberService.delete(id)
  304. return reply.send({ message: '删除会员成功' })
  305. } catch (error) {
  306. return reply.code(500).send({ message: '删除会员失败', error })
  307. }
  308. }
  309. async getAllMembers(request: FastifyRequest, reply: FastifyReply) {
  310. try {
  311. const members = await this.memberService.findAllMembers()
  312. return reply.send({ members })
  313. } catch (error) {
  314. return reply.code(500).send({ message: '获取所有会员失败', error })
  315. }
  316. }
  317. async updateStatus(
  318. request: FastifyRequest<{
  319. Params: { id: string }
  320. Body: { status: MemberStatus }
  321. }>,
  322. reply: FastifyReply
  323. ) {
  324. try {
  325. const id = parseInt(request.params.id)
  326. const { status } = request.body
  327. const updatedMember = await this.memberService.updateStatus(id, status)
  328. return reply.send({
  329. member: {
  330. id: updatedMember.id,
  331. userId: updatedMember.userId,
  332. email: updatedMember.email,
  333. phone: updatedMember.phone,
  334. vipLevel: updatedMember.vipLevel,
  335. status: updatedMember.status,
  336. vipExpireTime: updatedMember.vipExpireTime,
  337. lastLoginAt: updatedMember.lastLoginAt,
  338. createdAt: updatedMember.createdAt,
  339. updatedAt: updatedMember.updatedAt
  340. }
  341. })
  342. } catch (error) {
  343. return reply.code(500).send({ message: '更新会员状态失败', error })
  344. }
  345. }
  346. async updateLastLogin(request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply) {
  347. try {
  348. const id = parseInt(request.params.id)
  349. await this.memberService.updateLastLogin(id)
  350. return reply.send({ message: '更新最后登录时间成功' })
  351. } catch (error) {
  352. return reply.code(500).send({ message: '更新最后登录时间失败', error })
  353. }
  354. }
  355. async getStatistics(request: FastifyRequest, reply: FastifyReply) {
  356. try {
  357. const vipLevelStats = await this.memberService.countByVipLevel()
  358. const statusStats = await this.memberService.countByStatus()
  359. return reply.send({
  360. vipLevelStats,
  361. statusStats
  362. })
  363. } catch (error) {
  364. return reply.code(500).send({ message: '获取统计数据失败', error })
  365. }
  366. }
  367. }