AddAccount.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. const API = require("../../../lib/API.js");
  2. const db = require("../../../plugin/DataBase/db.js");
  3. const { BaseStdResponse } = require("../../../BaseStdResponse.js");
  4. const AccessControl = require("../../../lib/AccessControl.js");
  5. const { getJkesSettings } = require("../../../plugin/jkes/jkesSettings.js");
  6. const {
  7. DEFAULT_AUTO_RUN_DISTANCE_KM,
  8. validateAutoRunPresetForSave
  9. } = require("../../../plugin/jkes/autoRunAccountOptions.js");
  10. function pickBodyOrDb(bodyVal, dbVal, fallback) {
  11. if (bodyVal !== undefined && bodyVal !== null && bodyVal !== "") return bodyVal;
  12. if (dbVal !== undefined && dbVal !== null && dbVal !== "") return dbVal;
  13. return fallback;
  14. }
  15. class AddAccount extends API {
  16. constructor() {
  17. super();
  18. this.setPath('/Lepao/Account')
  19. this.setMethod('POST')
  20. this.emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
  21. this.banEmailList = ['icloud.com']
  22. }
  23. isQQ(str) {
  24. const reg = /^[1-9][0-9]{4,10}$/
  25. return reg.test(str)
  26. }
  27. // 生成 6 位数字 + 字母混合码
  28. async generateCode() {
  29. try {
  30. const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
  31. let code = ""
  32. for (let i = 0; i < 6; i++) {
  33. code += chars.charAt(Math.floor(Math.random() * chars.length))
  34. }
  35. let sql = 'SELECT id FROM lepao_extra WHERE bind_code = ?'
  36. let rows = await db.query(sql, [code])
  37. if (!rows)
  38. throw new Error('数据库错误,请稍后再试')
  39. if (rows.length > 0)
  40. return await this.generateCode()
  41. return code
  42. } catch (error) {
  43. throw error
  44. }
  45. }
  46. async onRequest(req, res) {
  47. let {
  48. uuid,
  49. session,
  50. student_num,
  51. email,
  52. id,
  53. area,
  54. auto_time,
  55. auto_run,
  56. target_count,
  57. auto_day,
  58. notice_type,
  59. notes,
  60. auto_run_distance_km,
  61. auto_run_distance_min_km,
  62. auto_run_distance_max_km,
  63. pace_min_sec_per_km,
  64. pace_max_sec_per_km
  65. } = req.body
  66. if ([uuid, session, student_num, auto_time, target_count, auto_day].some(value => value === '' || value === null || value === undefined))
  67. return res.json({
  68. ...BaseStdResponse.MISSING_PARAMETER
  69. })
  70. const targetKm = Number(target_count)
  71. if (!Number.isFinite(targetKm) || targetKm < 0 || targetKm > 200) {
  72. return res.json({
  73. ...BaseStdResponse.ERR,
  74. msg: '乐跑目标里程(公里)不在合法范围内(0–200)'
  75. })
  76. }
  77. if (notice_type === 'email') {
  78. if (!this.emailRegex.test(email)) {
  79. return res.json({
  80. ...BaseStdResponse.ERR,
  81. msg: '请检查邮箱格式是否正确'
  82. })
  83. }
  84. const emailDomain = email.split('@')[1].toLowerCase()
  85. if (this.banEmailList.includes(emailDomain))
  86. return res.json({
  87. ...BaseStdResponse.ERR,
  88. msg: `暂不支持使用 ${emailDomain} 域名的邮箱,请更换其他邮箱后重试`
  89. })
  90. }
  91. if (auto_run === 1 && (!Array.isArray(auto_day) || !auto_day.every(v => Number.isInteger(v) && v >= 0 && v <= 6)))
  92. return res.json({
  93. ...BaseStdResponse.ERR,
  94. msg: '自动乐跑日期格式不合法'
  95. })
  96. if (!await AccessControl.checkSession(uuid, session))
  97. return res.status(401).json({
  98. ...BaseStdResponse.ACCESS_DENIED
  99. })
  100. let mergeForPreset = null
  101. if (id) {
  102. const ownerRows = await db.query(
  103. "SELECT auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km FROM lepao_account WHERE id = ? AND create_user = ?",
  104. [id, uuid]
  105. );
  106. if (!ownerRows || ownerRows.length === 0) {
  107. return res.json({ ...BaseStdResponse.ERR, msg: "未找到该乐跑账号或无权限编辑" });
  108. }
  109. mergeForPreset = ownerRows[0];
  110. }
  111. let countSql =
  112. "SELECT id, create_user, total_num, term_num, auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km FROM lepao_account WHERE student_num = ?";
  113. let countRows = await db.query(countSql, [student_num])
  114. if (!countRows)
  115. return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败!数据库错误' })
  116. if (!id && countRows.length > 0) {
  117. mergeForPreset = countRows[0];
  118. }
  119. const cfg = getJkesSettings();
  120. const paceDefLo = cfg.paceRandomMinSecPerKm ?? 180;
  121. const paceDefHi = cfg.paceRandomMaxSecPerKm ?? 600;
  122. const minMissing =
  123. auto_run_distance_min_km === undefined ||
  124. auto_run_distance_min_km === null ||
  125. auto_run_distance_min_km === ''
  126. const maxMissing =
  127. auto_run_distance_max_km === undefined ||
  128. auto_run_distance_max_km === null ||
  129. auto_run_distance_max_km === ''
  130. const legacyOnly =
  131. minMissing &&
  132. maxMissing &&
  133. auto_run_distance_km !== undefined &&
  134. auto_run_distance_km !== null &&
  135. auto_run_distance_km !== ''
  136. const presetBody = legacyOnly
  137. ? {
  138. auto_run_distance_km,
  139. pace_min_sec_per_km: pickBodyOrDb(
  140. pace_min_sec_per_km,
  141. mergeForPreset?.pace_min_sec_per_km,
  142. paceDefLo
  143. ),
  144. pace_max_sec_per_km: pickBodyOrDb(
  145. pace_max_sec_per_km,
  146. mergeForPreset?.pace_max_sec_per_km,
  147. paceDefHi
  148. )
  149. }
  150. : {
  151. auto_run_distance_min_km: pickBodyOrDb(
  152. auto_run_distance_min_km,
  153. mergeForPreset?.auto_run_distance_min_km,
  154. DEFAULT_AUTO_RUN_DISTANCE_KM
  155. ),
  156. auto_run_distance_max_km: pickBodyOrDb(
  157. auto_run_distance_max_km,
  158. mergeForPreset?.auto_run_distance_max_km,
  159. DEFAULT_AUTO_RUN_DISTANCE_KM
  160. ),
  161. pace_min_sec_per_km: pickBodyOrDb(
  162. pace_min_sec_per_km,
  163. mergeForPreset?.pace_min_sec_per_km,
  164. paceDefLo
  165. ),
  166. pace_max_sec_per_km: pickBodyOrDb(
  167. pace_max_sec_per_km,
  168. mergeForPreset?.pace_max_sec_per_km,
  169. paceDefHi
  170. )
  171. }
  172. const preset = validateAutoRunPresetForSave(presetBody);
  173. if (!preset.ok) {
  174. return res.json({ ...BaseStdResponse.ERR, msg: preset.msg });
  175. }
  176. // 判断是否重复注册
  177. if (!id) {
  178. if (countRows.length !== 0 && countRows[0].create_user != null) {
  179. if (countRows[0].create_user !== uuid)
  180. return res.json({ ...BaseStdResponse.ERR, msg: '该乐跑账号已被其他用户绑定,请联系客服解绑' })
  181. return res.json({ ...BaseStdResponse.ERR, msg: '该乐跑账号您已绑定' })
  182. }
  183. }
  184. if (countRows.length !== 0) {
  185. if (auto_run === 1 && countRows[0].term_num >= targetKm && targetKm !== 0)
  186. return res.json({
  187. ...BaseStdResponse.ERR,
  188. msg: '该账号本月跑步里程已达到或超过预设目标里程,请增大目标后再试'
  189. })
  190. }
  191. const time = new Date().getTime()
  192. let sql, r, user_avatar
  193. // 生成用户头像
  194. if (email && email.split('@')[1].toLowerCase() === 'qq.com' && this.isQQ(email.split('@')[0])) {
  195. user_avatar = `https://q2.qlogo.cn/headimg_dl?dst_uin=${email}&spec=640`
  196. } else {
  197. let userSex = 2;
  198. const urows = await db.query("SELECT sex FROM users WHERE uuid = ? LIMIT 1", [uuid]);
  199. if (urows?.[0]?.sex === 1) userSex = 1;
  200. user_avatar =
  201. userSex === 1
  202. ? "https://lepao-cloud.xxoo365.top/view.php/aee85ff43fd30d0df03c6a7dd9797d22.png"
  203. : "https://lepao-cloud.xxoo365.top/view.php/fcb54dcc5e6209381e972ef73bdb4a93.png";
  204. }
  205. if (!id) {
  206. if (countRows.length !== 0) {
  207. sql = "UPDATE lepao_account SET create_user = ?, email = ?, user_avatar = ?, area = ?, auto_time = ?, auto_run = ?, target_count = ?, create_time = ?, update_time = ?, notes = ?, auto_day = ?, notice_type = ?, auto_run_distance_min_km = ?, auto_run_distance_max_km = ?, pace_min_sec_per_km = ?, pace_max_sec_per_km = ? WHERE id = ?";
  208. r = await db.query(sql, [
  209. uuid,
  210. email ?? "",
  211. user_avatar ?? "",
  212. area,
  213. auto_time,
  214. auto_run,
  215. targetKm,
  216. time,
  217. time,
  218. notes ?? "",
  219. JSON.stringify(auto_day),
  220. notice_type,
  221. preset.autoRunDistanceMinKm,
  222. preset.autoRunDistanceMaxKm,
  223. preset.paceMinSecPerKm,
  224. preset.paceMaxSecPerKm,
  225. countRows[0].id
  226. ]);
  227. }
  228. else {
  229. const bind_code = await this.generateCode()
  230. sql = "INSERT INTO lepao_account (student_num, email, user_avatar, area, auto_time, auto_run, target_count, create_user, create_time, notes, auto_day, notice_type, auto_run_distance_min_km, auto_run_distance_max_km, pace_min_sec_per_km, pace_max_sec_per_km) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
  231. r = await db.query(sql, [
  232. student_num,
  233. email ?? "",
  234. user_avatar ?? "",
  235. area,
  236. auto_time,
  237. auto_run,
  238. targetKm,
  239. uuid,
  240. time,
  241. notes ?? "",
  242. JSON.stringify(auto_day),
  243. notice_type,
  244. preset.autoRunDistanceMinKm,
  245. preset.autoRunDistanceMaxKm,
  246. preset.paceMinSecPerKm,
  247. preset.paceMaxSecPerKm
  248. ]);
  249. let faceSql = 'INSERT INTO lepao_extra (student_num, bind_code) VALUES (?, ?)'
  250. let faceRows = await db.query(faceSql, [student_num, bind_code])
  251. if (!faceRows || faceRows.affectedRows !== 1)
  252. return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败!数据库错误' })
  253. }
  254. } else {
  255. sql = "UPDATE lepao_account SET student_num = ?, email = ?, area = ?, auto_time = ?, target_count = ?, auto_run = ?, notes = ?, auto_day = ?, update_time = ?, notice_type = ?, auto_run_distance_min_km = ?, auto_run_distance_max_km = ?, pace_min_sec_per_km = ?, pace_max_sec_per_km = ? WHERE id = ? AND create_user = ?";
  256. r = await db.query(sql, [
  257. student_num,
  258. email ?? "",
  259. area,
  260. auto_time,
  261. targetKm,
  262. auto_run,
  263. notes ?? "",
  264. JSON.stringify(auto_day),
  265. time,
  266. notice_type,
  267. preset.autoRunDistanceMinKm,
  268. preset.autoRunDistanceMaxKm,
  269. preset.paceMinSecPerKm,
  270. preset.paceMaxSecPerKm,
  271. id,
  272. uuid
  273. ]);
  274. }
  275. try {
  276. if (r && r.affectedRows > 0) {
  277. const selectSql = `
  278. SELECT
  279. a.id, a.create_user, a.total_num, e.bind_code, e.bot_account
  280. FROM
  281. lepao_account a
  282. LEFT JOIN
  283. lepao_extra e
  284. ON
  285. a.student_num = e.student_num
  286. WHERE
  287. a.student_num = ?
  288. `
  289. const selectRows = await db.query(selectSql, [student_num])
  290. if (!selectRows)
  291. return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败!数据库错误' })
  292. res.json({
  293. ...BaseStdResponse.OK,
  294. id: r.insertId,
  295. data: {
  296. student_num,
  297. email,
  298. id,
  299. area,
  300. auto_time,
  301. auto_run,
  302. target_count: targetKm,
  303. auto_day,
  304. notice_type,
  305. notes,
  306. auto_run_distance_min_km: preset.autoRunDistanceMinKm,
  307. auto_run_distance_max_km: preset.autoRunDistanceMaxKm,
  308. pace_min_sec_per_km: preset.paceMinSecPerKm,
  309. pace_max_sec_per_km: preset.paceMaxSecPerKm,
  310. bind_code: selectRows.length !== 0 ? selectRows[0].bind_code : undefined,
  311. bot_account: selectRows.length !== 0 ? selectRows[0].bot_account : undefined
  312. }
  313. })
  314. } else {
  315. return res.json({ ...BaseStdResponse.ERR, msg: '添加乐跑账号失败!数据库错误' })
  316. }
  317. } catch (err) {
  318. this.logger.error(`添加乐跑账号失败!${err.stack}`)
  319. res.json({
  320. ...BaseStdResponse.ERR,
  321. msg: "添加乐跑账号失败!",
  322. });
  323. }
  324. }
  325. }
  326. module.exports.AddAccount = AddAccount;