user.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // 用户相关API处理
  2. const { getDatabase } = require('./sql');
  3. const fs = require('fs');
  4. const path = require('path');
  5. const { promisify } = require('util');
  6. const formidable = require('formidable');
  7. const mkdir = promisify(fs.mkdir);
  8. const access = promisify(fs.access);
  9. // 获取用户点数
  10. async function handleGetUserPoints(req, res) {
  11. const parsedUrl = require('url').parse(req.url, true);
  12. const { username } = parsedUrl.query;
  13. if (!username) {
  14. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  15. res.end(JSON.stringify({
  16. success: false,
  17. message: '缺少用户名参数'
  18. }));
  19. return;
  20. }
  21. try {
  22. const db = await getDatabase();
  23. const points = db.getUserPoints(username);
  24. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  25. res.end(JSON.stringify({
  26. success: true,
  27. points: points || 0
  28. }));
  29. } catch (error) {
  30. console.error('[User] 获取用户点数失败:', error);
  31. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  32. res.end(JSON.stringify({
  33. success: false,
  34. message: '获取点数失败: ' + error.message
  35. }));
  36. }
  37. }
  38. // 获取用户信息
  39. async function handleGetUserInfo(req, res) {
  40. const parsedUrl = require('url').parse(req.url, true);
  41. const { username } = parsedUrl.query;
  42. if (!username) {
  43. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  44. res.end(JSON.stringify({
  45. success: false,
  46. message: '缺少用户名参数'
  47. }));
  48. return;
  49. }
  50. try {
  51. const db = await getDatabase();
  52. const user = db.findUserByUsername(username);
  53. if (!user) {
  54. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  55. res.end(JSON.stringify({
  56. success: false,
  57. message: '用户不存在'
  58. }));
  59. return;
  60. }
  61. // 创建用户对象副本,不包含密码
  62. const userInfo = {
  63. id: user.id,
  64. username: user.username,
  65. phone: user.phone,
  66. avatar: user.avatar,
  67. points: user.points || 0,
  68. created_at: user.created_at,
  69. updated_at: user.updated_at
  70. };
  71. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  72. res.end(JSON.stringify({
  73. success: true,
  74. user: userInfo
  75. }));
  76. } catch (error) {
  77. console.error('[User] 获取用户信息失败:', error);
  78. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  79. res.end(JSON.stringify({
  80. success: false,
  81. message: '获取用户信息失败: ' + error.message
  82. }));
  83. }
  84. }
  85. // 更新用户信息
  86. async function handleUpdateUser(req, res) {
  87. let body = '';
  88. req.on('data', chunk => {
  89. body += chunk.toString();
  90. });
  91. req.on('end', async () => {
  92. try {
  93. const data = JSON.parse(body);
  94. const { username, phone } = data;
  95. if (!username) {
  96. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  97. res.end(JSON.stringify({
  98. success: false,
  99. message: '缺少用户名参数'
  100. }));
  101. return;
  102. }
  103. const db = await getDatabase();
  104. const user = db.findUserByUsername(username);
  105. if (!user) {
  106. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  107. res.end(JSON.stringify({
  108. success: false,
  109. message: '用户不存在'
  110. }));
  111. return;
  112. }
  113. // 更新手机号
  114. if (phone) {
  115. db.updateUser(user.id, { phone: phone });
  116. }
  117. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  118. res.end(JSON.stringify({
  119. success: true,
  120. message: '更新成功'
  121. }));
  122. } catch (error) {
  123. console.error('[User] 更新用户信息失败:', error);
  124. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  125. res.end(JSON.stringify({
  126. success: false,
  127. message: '更新失败: ' + error.message
  128. }));
  129. }
  130. });
  131. }
  132. // 上传头像
  133. async function handleUploadAvatar(req, res) {
  134. const form = formidable({
  135. uploadDir: path.join(__dirname, 'temp'),
  136. keepExtensions: true,
  137. maxFileSize: 5 * 1024 * 1024 // 5MB
  138. });
  139. form.parse(req, async (err, fields, files) => {
  140. if (err) {
  141. console.error('[User] 解析上传文件失败:', err);
  142. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  143. res.end(JSON.stringify({
  144. success: false,
  145. message: '上传失败: ' + err.message
  146. }));
  147. return;
  148. }
  149. const username = Array.isArray(fields.username) ? fields.username[0] : fields.username;
  150. const file = Array.isArray(files.avatar) ? files.avatar[0] : files.avatar;
  151. if (!username || !file) {
  152. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  153. res.end(JSON.stringify({
  154. success: false,
  155. message: '缺少参数'
  156. }));
  157. return;
  158. }
  159. try {
  160. const db = await getDatabase();
  161. const user = db.findUserByUsername(username);
  162. if (!user) {
  163. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  164. res.end(JSON.stringify({
  165. success: false,
  166. message: '用户不存在'
  167. }));
  168. return;
  169. }
  170. // 保存头像到用户目录
  171. const usersDir = path.join(__dirname, 'users');
  172. const userDir = path.join(usersDir, username.toLowerCase());
  173. await mkdir(userDir, { recursive: true });
  174. const avatarPath = path.join(userDir, 'avatar.png');
  175. await fs.promises.copyFile(file.filepath, avatarPath);
  176. // 删除临时文件
  177. await fs.promises.unlink(file.filepath);
  178. // 更新数据库
  179. const avatarUrl = `/users/${username.toLowerCase()}/avatar.png`;
  180. db.updateUser(user.id, { avatar: avatarUrl });
  181. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  182. res.end(JSON.stringify({
  183. success: true,
  184. avatar: avatarUrl,
  185. message: '头像更新成功'
  186. }));
  187. } catch (error) {
  188. console.error('[User] 保存头像失败:', error);
  189. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  190. res.end(JSON.stringify({
  191. success: false,
  192. message: '保存头像失败: ' + error.message
  193. }));
  194. }
  195. });
  196. }
  197. // 获取AI生图历史
  198. async function handleGetAIHistory(req, res) {
  199. const parsedUrl = require('url').parse(req.url, true);
  200. const { username } = parsedUrl.query;
  201. if (!username) {
  202. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  203. res.end(JSON.stringify({
  204. success: false,
  205. message: '缺少用户名参数'
  206. }));
  207. return;
  208. }
  209. try {
  210. const { getUserAIHistory } = require('./ai-queue');
  211. const history = await getUserAIHistory(username);
  212. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  213. res.end(JSON.stringify({
  214. success: true,
  215. history: history
  216. }));
  217. } catch (error) {
  218. console.error('[User] 获取AI历史失败:', error);
  219. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  220. res.end(JSON.stringify({
  221. success: false,
  222. message: '获取历史失败: ' + error.message
  223. }));
  224. }
  225. }
  226. // 处理充值请求
  227. async function handleRecharge(req, res) {
  228. let body = '';
  229. req.on('data', chunk => {
  230. body += chunk.toString();
  231. });
  232. req.on('end', async () => {
  233. try {
  234. const data = JSON.parse(body);
  235. const { username, points } = data;
  236. if (!username) {
  237. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  238. res.end(JSON.stringify({
  239. success: false,
  240. message: '缺少用户名参数'
  241. }));
  242. return;
  243. }
  244. if (!points || points <= 0) {
  245. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  246. res.end(JSON.stringify({
  247. success: false,
  248. message: '充值点数必须大于0'
  249. }));
  250. return;
  251. }
  252. const db = await getDatabase();
  253. const user = db.findUserByUsername(username);
  254. if (!user) {
  255. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  256. res.end(JSON.stringify({
  257. success: false,
  258. message: '用户不存在'
  259. }));
  260. return;
  261. }
  262. // 增加用户点数
  263. db.addPoints(username, points);
  264. // 获取更新后的点数
  265. const newPoints = db.getUserPoints(username);
  266. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  267. res.end(JSON.stringify({
  268. success: true,
  269. message: '充值成功',
  270. points: newPoints
  271. }));
  272. } catch (error) {
  273. console.error('[User] 充值失败:', error);
  274. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  275. res.end(JSON.stringify({
  276. success: false,
  277. message: '充值失败: ' + error.message
  278. }));
  279. }
  280. });
  281. }
  282. // 扣除用户点数
  283. async function handleDeductUserPoints(req, res) {
  284. let body = '';
  285. req.on('data', chunk => {
  286. body += chunk.toString();
  287. });
  288. req.on('end', async () => {
  289. try {
  290. const data = JSON.parse(body);
  291. const { username, points } = data;
  292. if (!username) {
  293. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  294. res.end(JSON.stringify({
  295. success: false,
  296. message: '缺少用户名参数'
  297. }));
  298. return;
  299. }
  300. if (!points || points <= 0) {
  301. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  302. res.end(JSON.stringify({
  303. success: false,
  304. message: '扣除点数必须大于0'
  305. }));
  306. return;
  307. }
  308. const db = await getDatabase();
  309. const user = db.findUserByUsername(username);
  310. if (!user) {
  311. res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
  312. res.end(JSON.stringify({
  313. success: false,
  314. message: '用户不存在'
  315. }));
  316. return;
  317. }
  318. // 检查点数是否足够
  319. const currentPoints = db.getUserPoints(username);
  320. if (currentPoints < points) {
  321. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  322. res.end(JSON.stringify({
  323. success: false,
  324. message: '点数不足'
  325. }));
  326. return;
  327. }
  328. // 扣除用户点数
  329. const deducted = db.deductPoints(username, points);
  330. if (!deducted) {
  331. res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
  332. res.end(JSON.stringify({
  333. success: false,
  334. message: '扣除点数失败'
  335. }));
  336. return;
  337. }
  338. // 获取更新后的点数
  339. const newPoints = db.getUserPoints(username);
  340. res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
  341. res.end(JSON.stringify({
  342. success: true,
  343. message: '扣除点数成功',
  344. points: newPoints
  345. }));
  346. } catch (error) {
  347. console.error('[User] 扣除点数失败:', error);
  348. res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
  349. res.end(JSON.stringify({
  350. success: false,
  351. message: '扣除点数失败: ' + error.message
  352. }));
  353. }
  354. });
  355. }
  356. module.exports = {
  357. handleGetUserPoints,
  358. handleGetUserInfo,
  359. handleDeductUserPoints,
  360. handleUpdateUser,
  361. handleUploadAvatar,
  362. handleGetAIHistory,
  363. handleRecharge
  364. };