user.js 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. /* eslint-disable eqeqeq */
  2. 'use strict';
  3. const shopController = require('./shop.js');
  4. const Decimal = require('decimal.js');
  5. // 用户控制器
  6. module.exports = class UserController extends shopController {
  7. // 使用模型
  8. get useModel() {
  9. const that = this;
  10. return that.app.model.Users;
  11. }
  12. /**
  13. * [registerValidate 用户注册验证器]
  14. * @return {[type]} [description]
  15. */
  16. get registerValidate() {
  17. const that = this;
  18. return {
  19. account_name: that.ctx.rules.name('账号')
  20. .required()
  21. .trim()
  22. .notEmpty(),
  23. password: that.ctx.rules.name('密码')
  24. .required()
  25. .trim()
  26. .notEmpty()
  27. .extend((field, value, row) => {
  28. row[field] = that.app.szjcomo.MD5(value);
  29. }),
  30. create_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s'))
  31. .required(),
  32. };
  33. }
  34. /**
  35. * [wxRegisterAdnLoginValidate 微信登录和注册验证器]
  36. * @return {[type]} [description]
  37. */
  38. get wxRegisterAdnLoginValidate() {
  39. const that = this;
  40. return {
  41. code: that.ctx.rules.name('微信授权code')
  42. .required()
  43. .notEmpty()
  44. .trim(),
  45. };
  46. }
  47. /**
  48. * [loginValidate 用户登录]
  49. * @return {[type]} [description]
  50. */
  51. get loginValidate() {
  52. const that = this;
  53. return {
  54. account_name: that.ctx.rules.name('用户账号')
  55. .required()
  56. .notEmpty()
  57. .trim(),
  58. password: that.ctx.rules.name('登录密码')
  59. .required()
  60. .notEmpty()
  61. .trim()
  62. .extend((field, value, row) => {
  63. row[field] = that.app.szjcomo.MD5(value);
  64. }),
  65. };
  66. }
  67. /**
  68. * 更新用户信息
  69. * @date:2023/10/20
  70. */
  71. get updateValidate() {
  72. const that = this;
  73. return {
  74. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  75. .number(),
  76. };
  77. }
  78. /**
  79. * [wxuserValidate 微信用户写入]
  80. * @return {[type]} [description]
  81. */
  82. get wxuserValidate() {
  83. const that = this;
  84. return {
  85. nickname: that.ctx.rules.name('用户昵称')
  86. .required()
  87. .notEmpty()
  88. .trim(),
  89. openid: that.ctx.rules.name('openid')
  90. .required()
  91. .notEmpty()
  92. .trim(),
  93. password: that.ctx.rules.default(that.app.szjcomo.MD5('123456'))
  94. .required(),
  95. account_name: that.ctx.rules.default(`${that.app.szjcomo.str_rand(6)}${that.app.szjcomo.date('His')}`)
  96. .required(),
  97. money: that.ctx.rules.default(0)
  98. .number(),
  99. intergral: that.ctx.rules.default(0)
  100. .number(),
  101. headimgurl: that.ctx.rules.default('')
  102. .required(),
  103. login_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s'))
  104. .required(),
  105. city: that.ctx.rules.default('')
  106. .required(),
  107. province: that.ctx.rules.default('')
  108. .required(),
  109. country: that.ctx.rules.default('')
  110. .required(),
  111. sex: that.ctx.rules.default(0)
  112. .number(),
  113. subscribe: that.ctx.rules.default(0)
  114. .number(),
  115. unionid: that.ctx.rules.default('')
  116. .required(),
  117. create_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s'))
  118. .required(),
  119. };
  120. }
  121. /**
  122. * [wxloginURLValidate 获取微信登录地址]
  123. * @return {[type]} [description]
  124. */
  125. get wxloginURLValidate() {
  126. const that = this;
  127. return {
  128. page_uri: that.ctx.rules.name('当前地址')
  129. .required()
  130. .notEmpty()
  131. .trim(),
  132. };
  133. }
  134. /**
  135. * [userMoneyValidate 获取用户余额]
  136. * @return {[type]} [description]
  137. */
  138. get userMoneyValidate() {
  139. const that = this;
  140. return {
  141. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  142. .number(),
  143. page: that.ctx.rules.default(1)
  144. .number(),
  145. limit: that.ctx.rules.default(50)
  146. .number(),
  147. };
  148. }
  149. get luckyValidate() {
  150. const that = this;
  151. return {
  152. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  153. .number(),
  154. };
  155. }
  156. get userTransferValidate() {
  157. const that = this;
  158. return {
  159. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  160. .number(),
  161. diningCoinCode: that.ctx.rules.default('')
  162. .required(),
  163. page: that.ctx.rules.default(1)
  164. .number(),
  165. limit: that.ctx.rules.default(50)
  166. .number(),
  167. };
  168. }
  169. get businessCashCoinValidate() {
  170. const that = this;
  171. return {
  172. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  173. .number(),
  174. };
  175. }
  176. // 2023/1/13 提现验证器
  177. get cashOutValidate() {
  178. const that = this;
  179. return {
  180. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  181. .number(),
  182. cash_amount: that.ctx.rules.name('提现金额')
  183. .required()
  184. .notEmpty()
  185. .number(),
  186. remark: that.ctx.rules.name('备注说明')
  187. .default('')
  188. .trim(),
  189. };
  190. }
  191. // 2023/2/28 餐币核销验证
  192. get coinTransferValidate() {
  193. const that = this;
  194. return {
  195. user_id: that.ctx.rules.default(that.service.shop.getWebUserId())
  196. .number(),
  197. coinAmount: that.ctx.rules.name('核销收取餐币')
  198. .required()
  199. .notEmpty()
  200. .number(),
  201. time: that.ctx.rules.name('餐币二维码刷新时间')
  202. .required()
  203. .notEmpty()
  204. .number(),
  205. couponCode: that.ctx.rules.name('餐币二维码特征')
  206. .required()
  207. .notEmpty(),
  208. };
  209. }
  210. /**
  211. * [login 用户登录 - 账号密码]
  212. * @return {[type]} [description]
  213. */
  214. async login() {
  215. const that = this;
  216. try {
  217. const data = await that.ctx.validate(that.loginValidate, await that.ctx.postParse());
  218. const user = await that.useModel.findOne({
  219. where: { account_name: data.account_name, password: data.password },
  220. // include: [
  221. // { model: that.app.model.ProxyApplyLogs, as: 'proxyApplyLogs', attributes: [ 'verify_status' ] },
  222. // ],
  223. attributes: [ 'user_id', 'account_name', 'nickname', 'headimgurl', 'openid', 'intergral', 'is_proxy', 'partner_id', 'is_office', 'is_family', 'lucky_time' ],
  224. raw: true,
  225. });
  226. if (!user) throw new Error('登录失败,请检查账号密码是否正确');
  227. user.isNew = false;
  228. const result = await that.loginHandle(user);
  229. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  230. } catch (err) {
  231. console.log(err);
  232. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  233. }
  234. }
  235. /**
  236. * [loginHandle 登录处理]
  237. * @param {Object} user [description]
  238. * @return {[type]} [description]
  239. */
  240. async loginHandle(user = {}) {
  241. const that = this;
  242. // 2023/2/28 UIOT现金券发放
  243. that.service.coupon.dispatchCoupon(user.user_id);
  244. const token = that.app.jwt.sign(user, that.app.config.jwt.secret, { expiresIn: '24h' });
  245. const result = { token, user };
  246. return result;
  247. }
  248. /**
  249. * [register 用户注册]
  250. * @return {[type]} [description]
  251. */
  252. async register() {
  253. const that = this;
  254. try {
  255. const data = await that.ctx.validate(that.registerValidate, await that.ctx.postParse());
  256. const createBean = that.app.comoBean.instance(data, {});
  257. const result = await that.service.manager.create(createBean, that.useModel, '注册失败,请稍候重试');
  258. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  259. } catch (err) {
  260. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  261. }
  262. }
  263. /**
  264. * [wxRegisterAdnLogin 用户登录 - 微信注册登录]
  265. * @return {[type]} [description]
  266. */
  267. async wxRegisterAdnLogin() {
  268. const that = this;
  269. try {
  270. const appid = await that.service.configs.getConfigValue('wechat_account_appid');
  271. const secret = await that.service.configs.getConfigValue('wechat_account_secret');
  272. const data = await that.ctx.validate(that.wxRegisterAdnLoginValidate, await that.ctx.getParse());
  273. const result = await that.service.wechat.authLogin(appid, secret, data.code);
  274. if (result.errcode) throw new Error(`微信通讯失败,错误信息:${result.errmsg}`);
  275. const userInfo = await that.service.wechat.authUserInfo(result);
  276. const user = await that.wxRegisterAdnLoginHandle(userInfo, data.inviteCode);
  277. const loginRes = await that.loginHandle(user);
  278. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', loginRes, false));
  279. } catch (err) {
  280. await that.logs('user.js', err);
  281. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  282. }
  283. }
  284. /**
  285. * [wxloginURL 微信登录地址]
  286. * @return {[type]} [description]
  287. */
  288. async wxloginURL() {
  289. const that = this;
  290. try {
  291. const data = await that.ctx.validate(that.wxloginURLValidate, await that.ctx.postParse());
  292. const appid = await that.service.configs.getConfigValue('wechat_account_appid');
  293. const curarr = data.page_uri.split('#');
  294. const result = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${encodeURIComponent(curarr[0] + '#/shop/wxauth')}&response_type=code&scope=snsapi_userinfo&state=${that.app.szjcomo.base64_encode(data.page_uri)}#wechat_redirect`;
  295. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  296. } catch (err) {
  297. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  298. }
  299. }
  300. /**
  301. * [wxRegisterAdnLoginHandle 注册并登录]
  302. * @param {Object} userInfo [description]
  303. * @param {Number} inviteCode
  304. * @return {[type]} [description]
  305. */
  306. async wxRegisterAdnLoginHandle(userInfo = {}, inviteCode = -1) {
  307. const that = this;
  308. const inviter_id = inviteCode;
  309. // console.log('==========inviteCode========== : ' + inviter_id);
  310. const options = {
  311. where: { openid: userInfo.openid },
  312. attributes: [ 'user_id', 'account_name', 'nickname', 'headimgurl', 'openid', 'partner_id', 'is_office', 'is_family', 'lucky_time' ],
  313. raw: true,
  314. };
  315. let user = await that.useModel.findOne(options);
  316. if (!user) {
  317. // 2022/9/27 新用户注册 写入信息
  318. user = await that.writeWxUser(userInfo);
  319. user.isNew = true;
  320. // 2022/9/27: 新用户红包奖励8.8元
  321. await that.service.shop.userMoneyAdd(user.user_id, 8.8, null, '新用户注册奖励', 0, 1, -1);
  322. // 2022/9/29 受邀注册奖励 和 邀请新用户奖励
  323. if (inviter_id > 0) {
  324. try {
  325. const inviterInfo = await that.useModel.findOne({
  326. where: { user_id: inviter_id },
  327. attributes: [ 'user_id', 'nickname', 'headimgurl' ],
  328. raw: true,
  329. });
  330. await that.service.shop.userMoneyAdd(user.user_id, 1.68, null, '受邀注册奖励', 0, 2, inviter_id, inviterInfo.nickname, inviterInfo.headimgurl);
  331. await that.service.shop.userMoneyAdd(inviter_id, 1.68, null, '邀请新用户奖励', 0, 3, user.user_id, user.nickname, user.headimgurl);
  332. // 2022/11/17 邀请关系绑定
  333. await that.service.inviter.addRelUserInviter(user.user_id, inviter_id);
  334. } catch (e) {
  335. // 邀请人id不存在
  336. }
  337. }
  338. } else {
  339. user.isNew = false;
  340. // 2023/8/25 更新登录时间
  341. const updateBean = await that.app.comoBean.instance({
  342. login_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  343. update_ttime: that.app.szjcomo.date('Y-m-d H:i:s'),
  344. unionid: userInfo.unionid,
  345. }, { where: { user_id: user.user_id } });
  346. await that.service.base.update(updateBean, that.useModel, '微信用户登录时间更新失败,请重试');
  347. }
  348. return user;
  349. }
  350. /**
  351. * [writeWxUser 写入微信用户]
  352. * @param {Object} userInfo [description]
  353. * @return {[type]} [description]
  354. */
  355. async writeWxUser(userInfo = {}) {
  356. const that = this;
  357. const data = await that.ctx.validate(that.wxuserValidate, userInfo);
  358. const createBean = await that.app.comoBean.instance(data);
  359. const result = await that.service.base.create(createBean, that.useModel, '添加微信用户失败,请重试');
  360. return {
  361. user_id: result.dataValues.user_id,
  362. account_name: result.dataValues.account_name,
  363. nickname: result.dataValues.nickname,
  364. headimgurl: result.dataValues.headimgurl,
  365. openid: result.dataValues.openid,
  366. unionid: result.dataValues.unionid,
  367. is_office: result.dataValues.is_office,
  368. is_family: result.dataValues.is_family,
  369. };
  370. }
  371. /**
  372. * [userMoney 获取用户余额]
  373. * @return {[type]} [description]
  374. */
  375. async userMoney() {
  376. const that = this;
  377. try {
  378. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  379. const result = await that.service.shop.getUserMoney(data.user_id);
  380. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  381. } catch (err) {
  382. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  383. }
  384. }
  385. /**
  386. * [userMoney 获取用户账户余额]
  387. * @return {[type]} [description]
  388. */
  389. async userAccount() {
  390. const that = this;
  391. try {
  392. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  393. const result = await that.service.shop.getUserAccount(data.user_id);
  394. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  395. } catch (err) {
  396. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  397. }
  398. }
  399. /**
  400. * [userMoneyLog 用户资金明细]
  401. * @return {[type]} [description]
  402. */
  403. async userMoneyLog() {
  404. const that = this;
  405. try {
  406. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  407. const selectBean = await that.app.comoBean.instance(data, {
  408. offset: (data.page - 1) * data.limit,
  409. limit: data.limit,
  410. where: { user_id: data.user_id },
  411. order: [ [ 'log_id', 'desc' ] ],
  412. attributes: [ 'log_id', 'log_desc', 'change_time', 'money', 'type', 'inviter_id', 'inviter_name', 'inviter_img' ],
  413. });
  414. const result = await that.service.base.select(selectBean, that.app.model.UsersMoneyLogs, '查询资金明细失败,请稍候重试', true, true);
  415. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  416. } catch (err) {
  417. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  418. }
  419. }
  420. /**
  421. * [userMoneyLog 用户分佣明细]
  422. * @return {[type]} [description]
  423. */
  424. async userCommissionLog() {
  425. const that = this;
  426. try {
  427. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  428. const selectBean = await that.app.comoBean.instance(data, {
  429. // offset: (data.page - 1) * data.limit, limit: data.limit,
  430. where: { user_id: data.user_id },
  431. order: [ [ 'log_id', 'desc' ] ],
  432. include: [ {
  433. model: that.app.model.Orders,
  434. attributes: [ 'order_id', 'order_status', 'order_amount', ],
  435. as: 'order'
  436. } ],
  437. attributes: [ 'log_desc', 'create_time', 'commission', 'type', 'inviter_id', 'inviter_name', 'inviter_img' ],
  438. });
  439. // 2022/11/28 直接查询所有数据 不分页
  440. const result = await that.service.base.select(selectBean, that.app.model.UsersCommissionLogs, '查询分佣明细失败,请稍候重试', false, true);
  441. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  442. } catch (err) {
  443. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  444. }
  445. }
  446. /**
  447. * [userMoneyLog 用户餐币明细]
  448. * @return {[type]} [description]
  449. */
  450. async coinDetail() {
  451. const that = this;
  452. try {
  453. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  454. const selectBean = await that.app.comoBean.instance(data, {
  455. where: { user_id: data.user_id },
  456. order: [ [ 'log_id', 'desc' ] ],
  457. });
  458. // 2022/11/28 直接查询所有数据 不分页
  459. const result = await that.service.base.select(selectBean, that.app.model.DinnerCoinLogs, '查询分佣明细失败,请稍候重试', false, true);
  460. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  461. } catch (err) {
  462. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  463. }
  464. }
  465. // 2023/2/28 用户卡券账户列表
  466. async userCoupon() {
  467. const that = this;
  468. try {
  469. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  470. const selectBean = await that.app.comoBean.instance(data, {
  471. where: { user_id: data.user_id, expired: false },
  472. });
  473. // 2023/2/28 直接查询所有数据 不分页
  474. const result = await that.service.base.select(selectBean, that.app.model.DinnerCoins, '查询餐饮币账户失败,请稍候重试', false, true);
  475. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  476. } catch (err) {
  477. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  478. }
  479. }
  480. /**
  481. * 用户指定商家餐币账户余额
  482. * @return {Promise<*>}
  483. */
  484. async userCouldTransferCoin() {
  485. const that = this;
  486. try {
  487. const data = await that.ctx.validate(that.userTransferValidate, await that.ctx.getParse());
  488. const res = await that.getCouldTransferCoin(data);
  489. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', { couldTransferCoins: res.account }, false));
  490. } catch (err) {
  491. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  492. }
  493. }
  494. /**
  495. * 用户可在指定商家消费的餐币账户余额
  496. * @param data
  497. */
  498. async getCouldTransferCoin(data) {
  499. const that = this;
  500. if (!data.couponCode) {
  501. throw new Error('参数错误');
  502. }
  503. const userInfo = await that.app.model.Users.findOne({
  504. where: { openid: data.couponCode },
  505. attributes: [ 'user_id' ],
  506. });
  507. if (!userInfo || userInfo.user_id < 0) {
  508. throw new Error('参数错误');
  509. }
  510. const businessInfo = await that.app.model.Users.findOne({
  511. where: { user_id: data.user_id },
  512. attributes: [ 'partner_id' ],
  513. });
  514. if (!businessInfo || businessInfo.partner_id < 1) {
  515. throw new Error('抱歉,您不是源森家具的合作商户,无权收款。');
  516. }
  517. const seq = that.app.Sequelize;
  518. const selectBean = await that.app.comoBean.instance({}, {
  519. where: {
  520. user_id: userInfo.user_id,
  521. // partner_id: businessInfo.partner_id,
  522. // 2023/4/11 补充通用餐币
  523. partner_id: { [seq.Op.in]: [ businessInfo.partner_id, 1 ] },
  524. expired: false,
  525. },
  526. attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ],
  527. });
  528. const result = await that.service.base.select(selectBean, that.app.model.DinnerCoins, '查询餐饮币账户余额失败,请稍候重试', false, false);
  529. const res = JSON.parse(JSON.stringify(result));
  530. res.customer_id = userInfo.user_id;
  531. res.partner_id = businessInfo.partner_id;
  532. res.business_id = data.user_id;
  533. return res;
  534. }
  535. /**
  536. * 商家可提现餐币金额
  537. * @return {Promise<*>}
  538. */
  539. async businessDiningCoinCouldCash() {
  540. const that = this;
  541. try {
  542. const data = await that.ctx.validate(that.businessCashCoinValidate, await that.ctx.getParse());
  543. const res = await that.getDiningCoinCouldCash(data);
  544. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', res, false));
  545. } catch (err) {
  546. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  547. }
  548. }
  549. /**
  550. * 商家可提现餐币金额
  551. * @param data
  552. */
  553. async getDiningCoinCouldCash(data) {
  554. const that = this;
  555. const businessInfo = await that.app.model.Users.findOne({
  556. where: { user_id: data.user_id },
  557. });
  558. if (!businessInfo || businessInfo.partner_id < 0) {
  559. throw new Error('您的账号还没有绑定的合作商铺哦');
  560. }
  561. const partnerInfo = await that.app.model.PartnerInfo.findOne({
  562. where: { id: businessInfo.partner_id },
  563. });
  564. if (!partnerInfo || partnerInfo.user_id !== data.user_id) {
  565. throw new Error('您的账号没有权限提现餐费哦');
  566. }
  567. const seq = that.app.Sequelize;
  568. const currentDateTime = that.app.szjcomo.date('Y-m-d H:i:s');
  569. const sevenDayTime = 24 * 60 * 60 * 1000; // 2023/3/1 24小时后可提现
  570. const endTimeStamp = Date.parse(currentDateTime) - sevenDayTime;
  571. const endDateTime = that.app.szjcomo.date('Y-m-d H:i:s', endTimeStamp / 1000);
  572. // 2023/3/1 查询24小时前的所得餐币总和 以及所有时间提现的总和 再求和 即为 可提现餐币金额
  573. // todo : 特殊情况 商家管理员有自家绑定的商铺 餐币消费支出
  574. const selectBean = await that.app.comoBean.instance({}, {
  575. where: {
  576. user_id: businessInfo.user_id,
  577. partner_id: businessInfo.partner_id,
  578. create_time: { [seq.Op.lte]: endDateTime },
  579. account: { [seq.Op.gte]: 0 },
  580. },
  581. attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ],
  582. });
  583. const coinResult = await that.service.base.select(selectBean, that.app.model.DinnerCoinLogs, '查询商家可提现餐币金额失败,请稍候重试', false, false);
  584. const selectBean2 = await that.app.comoBean.instance({}, {
  585. where: {
  586. user_id: businessInfo.user_id,
  587. partner_id: businessInfo.partner_id,
  588. account: { [seq.Op.lte]: 0 },
  589. type: { [seq.Op.ne]: -1 },
  590. },
  591. attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ],
  592. });
  593. const cashResult = await that.service.base.select(selectBean2, that.app.model.DinnerCoinLogs, '查询商家可提现餐币金额失败,请稍候重试', false, false);
  594. const coinRes = JSON.parse(JSON.stringify(coinResult));
  595. const cashRes = JSON.parse(JSON.stringify(cashResult));
  596. const couldCash = new Decimal(coinRes.account ? coinRes.account : 0)
  597. .add(new Decimal(cashRes.account ? cashRes.account : 0))
  598. .toNumber();
  599. coinRes.all_coin_could_cash = couldCash > 0 ? couldCash : 0;
  600. coinRes.all_cash = cashRes.account;
  601. coinRes.partner_id = businessInfo.partner_id;
  602. coinRes.partner_fees = partnerInfo.partner_fees;
  603. return coinRes;
  604. }
  605. /**
  606. * 商家申请核销顾客卡券
  607. * @return {Promise<void>}
  608. */
  609. async coinTransfer() {
  610. const that = this;
  611. const seq = that.app.Sequelize;
  612. const transaction = await that.app.model.transaction();
  613. try {
  614. const data = await that.ctx.validate(that.coinTransferValidate, await that.ctx.postParse());
  615. // 2023/2/28 获取可核销餐币
  616. const intervalTime = Date.parse(that.app.szjcomo.date('Y-m-d H:i:s')) - data.time;
  617. if (intervalTime > 10 * 60 * 1000) {
  618. throw new Error('顾客卡券二维码已超时失效,请重新扫码!');
  619. }
  620. const transferParams = await that.getCouldTransferCoin(data);
  621. if (data.coinAmount > 0 && data.coinAmount <= transferParams.account) {
  622. // 2023/2/28 核销卡券的商家信息
  623. const partnerInfo = await that.app.model.PartnerInfo.findOne({
  624. where: { id: transferParams.partner_id },
  625. });
  626. // 2024/9/13 被核销用户卡券信息
  627. const result = await that.app.model.DinnerCoins.findOne({
  628. where: {
  629. user_id: transferParams.customer_id,
  630. // partner_id: transferParams.partner_id,
  631. partner_id: { [seq.Op.in]: [ transferParams.partner_id, 1 ] },
  632. expired: false,
  633. }
  634. });
  635. if (result) {
  636. // 2024/9/13 核销 更新顾客账户
  637. const customerAccountBean = await that.app.comoBean.instance({
  638. expired: true,
  639. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  640. expired_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  641. }, {
  642. where: {
  643. user_id: transferParams.customer_id,
  644. partner_id: transferParams.partner_id,
  645. expired: false,
  646. }, transaction,
  647. });
  648. await that.service.base.update(customerAccountBean, that.app.model.DinnerCoins, '顾客餐饮币余额更新失败,请重试');
  649. } else {
  650. throw new Error('当前客户没有符合条件的卡券或者卡券已过期!');
  651. }
  652. await transaction.commit();
  653. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', {
  654. expired: true,
  655. }, false));
  656. }
  657. throw new Error('输入的核销金额有误,请稍后重试');
  658. } catch (e) {
  659. if (transaction) transaction.rollback();
  660. return that.ctx.appJson(that.app.szjcomo.appResult(e.message));
  661. }
  662. }
  663. // 2023/3/1 餐币划账 具体逻辑和操作
  664. async doTransferCoins(that, transferParams, coinAmount, partnerInfo, transaction, customerAccounts, index) {
  665. // 2023/3/6 获取顾客信息
  666. const customerInfo = await that.app.model.Users.findOne({
  667. where: { user_id: transferParams.customer_id },
  668. transaction,
  669. raw: true,
  670. });
  671. // 2023/3/1 扣去顾客餐币
  672. await that.service.diningCoin.addDiningCoinChangeLog({
  673. user_id: transferParams.customer_id,
  674. order_id: -1,
  675. partner_id: customerAccounts[index].partner_id,
  676. type: 3,
  677. account: -coinAmount,
  678. log_desc: partnerInfo.name + ' 消费',
  679. }, transaction);
  680. // 2023/3/1 更新顾客账户
  681. const customerAccountBean = await that.app.comoBean.instance({
  682. account: customerAccounts[index].account - coinAmount,
  683. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  684. }, {
  685. where: {
  686. user_id: transferParams.customer_id,
  687. ori_partner: customerAccounts[index].ori_partner,
  688. partner_id: customerAccounts[index].partner_id,
  689. expired: false,
  690. }, transaction,
  691. });
  692. if (customerAccounts[index].account === coinAmount) {
  693. // 2023/3/1 删除0元账户
  694. await that.service.base.delete(customerAccountBean, that.app.model.DinnerCoins, '顾客餐饮币清零更新失败,请重试');
  695. } else {
  696. await that.service.base.update(customerAccountBean, that.app.model.DinnerCoins, '顾客餐饮币余额更新失败,请重试');
  697. }
  698. // 2023/3/1 划入商家账户记录
  699. const businessCoinAmount = coinAmount * (customerAccounts[index].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default);
  700. await that.service.diningCoin.addDiningCoinChangeLog({
  701. user_id: transferParams.business_id,
  702. order_id: -1,
  703. partner_id: transferParams.partner_id,
  704. type: 4,
  705. account: businessCoinAmount,
  706. log_desc: '核销收取餐币',
  707. ori_partner: customerAccounts[index].ori_partner,
  708. customer_id: customerInfo.user_id,
  709. customer_name: customerInfo.nickname,
  710. customer_img: customerInfo.headimgurl,
  711. }, transaction);
  712. // 2023/2/28 查询商家餐饮币账户列表 添加 或 更新账户余额
  713. const info = await that.app.model.DinnerCoins.findOne({
  714. where: {
  715. user_id: transferParams.business_id,
  716. // ori_partner: customerAccounts[index].ori_partner,
  717. partner_id: transferParams.partner_id,
  718. expired: false,
  719. },
  720. transaction,
  721. raw: true,
  722. });
  723. if (!info) {
  724. // 2023/2/27 没有对应类型的餐饮币 则插入该类型的餐饮币账户
  725. const createData = {
  726. user_id: transferParams.business_id,
  727. account: businessCoinAmount,
  728. // ori_partner: customerAccounts[index].ori_partner,
  729. ori_partner: true,
  730. partner_id: transferParams.partner_id,
  731. create_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  732. expired: false,
  733. expired_time: that.app.szjcomo.date('Y-m-d H:i:s', parseInt(+new Date() + '') / 1000 + 90 * 24 * 60 * 60),
  734. };
  735. createData.partner_name = partnerInfo.name;
  736. createData.partner_address = partnerInfo.address;
  737. createData.partner_tel = partnerInfo.tel_num;
  738. createData.partner_opening_time = partnerInfo.opening_time;
  739. const createBean = await that.app.comoBean.instance(createData, { transaction });
  740. await that.service.base.create(createBean, that.app.model.DinnerCoins, '餐饮币发放失败,请重试');
  741. } else {
  742. const updateBean = await that.app.comoBean.instance({
  743. account: info.account + businessCoinAmount,
  744. update_time: that.app.szjcomo.date('Y-m-d H:i:s'),
  745. expired: false,
  746. expired_time: that.app.szjcomo.date('Y-m-d H:i:s', parseInt(+new Date() + '') / 1000 + 90 * 24 * 60 * 60),
  747. }, {
  748. where: {
  749. user_id: transferParams.business_id,
  750. // ori_partner: customerAccounts[index].ori_partner,
  751. partner_id: transferParams.partner_id,
  752. }, transaction,
  753. });
  754. await that.service.base.update(updateBean, that.app.model.DinnerCoins, '餐饮币余额更新失败,请重试');
  755. }
  756. }
  757. /**
  758. * 用户可提现金额(求和)
  759. * @return {Promise<*>}
  760. */
  761. async userCommissionCouldCash() {
  762. const that = this;
  763. try {
  764. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  765. const result = await that.getCouldCashCommission(data.user_id);
  766. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  767. } catch (err) {
  768. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  769. }
  770. }
  771. /**
  772. * 获取用户可提现金额(求和)
  773. * @return {Promise<void>}
  774. */
  775. async getCouldCashCommission(user_id = -1) {
  776. if (user_id <= 0) {
  777. throw new Error('参数错误');
  778. }
  779. const that = this;
  780. const seq = that.app.Sequelize;
  781. // 2024/2/26 所有分佣金额
  782. const selectBean = await that.app.comoBean.instance({ user_id }, {
  783. where: { user_id, commission: { [seq.Op.gte]: 0 } },
  784. include: [ {
  785. model: that.app.model.Orders,
  786. where: { order_status: { [seq.Op.in]: [ 1, 2, 3, 4 ] } },
  787. attributes: [ 'order_id', 'order_status', 'order_amount', ],
  788. as: 'order'
  789. } ],
  790. attributes: [ 'user_id', [ seq.fn('sum', seq.col('commission')), 'all_commission' ] ],
  791. });
  792. const allCommissionResult = await that.service.base.select(selectBean, that.app.model.UsersCommissionLogs, '查询佣金失败,请稍候重试', false, false);
  793. // 2024/2/26 订单完成可提现金额
  794. const selectBean3 = await that.app.comoBean.instance({ user_id }, {
  795. where: { user_id, commission: { [seq.Op.gte]: 0 } },
  796. include: [ {
  797. model: that.app.model.Orders,
  798. where: { order_status: { [seq.Op.in]: [ 3, 4 ] } },
  799. attributes: [ 'order_id', 'order_status', 'order_amount', ],
  800. as: 'order'
  801. } ],
  802. attributes: [ 'user_id', [ seq.fn('sum', seq.col('commission')), 'order_done_commission' ] ],
  803. });
  804. const orderCommissionResult = await that.service.base.select(selectBean3, that.app.model.UsersCommissionLogs, '查询佣金失败,请稍候重试', false, false);
  805. // 2024/2/26 已提现金额
  806. const selectBean2 = await that.app.comoBean.instance({ user_id }, {
  807. where: { user_id, commission: { [seq.Op.lte]: 0 }, type: { [seq.Op.ne]: -1 } }, // type-1提现失败类型
  808. attributes: [ 'user_id', [ seq.fn('sum', seq.col('commission')), 'all_cash' ] ],
  809. });
  810. const cashResult = await that.service.base.select(selectBean2, that.app.model.UsersCommissionLogs, '查询佣金失败,请稍候重试', false, false);
  811. const allCommRes = JSON.parse(JSON.stringify(allCommissionResult));
  812. const orderCommRes = JSON.parse(JSON.stringify(orderCommissionResult));
  813. const cashRes = JSON.parse(JSON.stringify(cashResult));
  814. // 2024/2/26 待提现分佣金额
  815. cashRes.all_commission_tobe_cash = new Decimal(allCommRes.all_commission ? allCommRes.all_commission : 0)
  816. .add(new Decimal(cashRes.all_cash ? cashRes.all_cash : 0))
  817. .toNumber();
  818. // 2024/2/26 可提现分佣金额
  819. cashRes.all_commission_could_cash = new Decimal(orderCommRes.order_done_commission ? orderCommRes.order_done_commission : 0)
  820. .add(new Decimal(cashRes.all_cash ? cashRes.all_cash : 0))
  821. .toNumber();
  822. cashRes.all_commission = allCommRes.all_commission;
  823. // 2024/2/26 待订单确认收货分佣
  824. cashRes.all_commission_order_not_done = new Decimal(allCommRes.all_commission ? allCommRes.all_commission : 0)
  825. .add(new Decimal(cashRes.all_cash ? cashRes.all_cash : 0))
  826. .sub(cashRes.all_commission_could_cash)
  827. .toNumber();
  828. return cashRes;
  829. }
  830. /**
  831. * 用户佣金提现
  832. * @return {Promise<void>}
  833. */
  834. async userCashOut() {
  835. const that = this;
  836. try {
  837. const data = await that.ctx.validate(that.cashOutValidate, await that.ctx.postParse());
  838. // 2023/1/17 获取可提现佣金额度
  839. const res = await that.getCouldCashCommission(data.user_id);
  840. if (data.cash_amount >= 0.1 && data.cash_amount <= res.all_commission_could_cash) {
  841. // 2023/1/31 发起提现
  842. const result = await that.service.wxPay.transfer(data);
  843. // 2023/1/31 提现状态
  844. if (result.status != 200) {
  845. throw new Error('提现转账出现异常,请联系客服后再重试');
  846. }
  847. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  848. }
  849. throw new Error('输入提现金额有误,请稍后重试');
  850. } catch (e) {
  851. return that.ctx.appJson(that.app.szjcomo.appResult(e.message));
  852. }
  853. }
  854. /**
  855. * 用户佣金转电子餐费
  856. * @return {Promise<void>}
  857. */
  858. async commission2DiningCoin() {
  859. // 2023/11/17 todo: 佣金转电子餐费
  860. }
  861. /**
  862. * 商家餐币提现
  863. * @return {Promise<*>}
  864. */
  865. async userCoinCashOut() {
  866. const that = this;
  867. try {
  868. const data = await that.ctx.validate(that.cashOutValidate, await that.ctx.postParse());
  869. // 2023/1/17 获取可提现佣金额度
  870. const res = await that.getDiningCoinCouldCash(data);
  871. data.partner_id = res.partner_id;
  872. if (data.cash_amount >= 0.1 && data.cash_amount <= (res.all_coin_could_cash - res.partner_fees)) {
  873. // 2023/1/31 发起提现
  874. const result = await that.service.businessPayService.transfer(data);
  875. // 2023/1/31 提现状态
  876. if (result.status != 200) {
  877. throw new Error('提现转账出现异常,请联系客服后再重试');
  878. }
  879. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  880. }
  881. throw new Error('输入提现金额有误,请稍后重试');
  882. } catch (e) {
  883. return that.ctx.appJson(that.app.szjcomo.appResult(e.message));
  884. }
  885. }
  886. /**
  887. * [newUserBenefits 查询新用户福利]
  888. * @return {[type]} [description]
  889. */
  890. async newUserBenefits() {
  891. const that = this;
  892. try {
  893. const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse());
  894. const seq = that.app.Sequelize;
  895. const selectBean = await that.app.comoBean.instance(data, {
  896. offset: (data.page - 1) * data.limit, limit: data.limit, where: {
  897. user_id: data.user_id,
  898. type: { [seq.Op.in]: [ 1, 2 ] },
  899. },
  900. order: [ [ 'type', 'asc' ] ],
  901. });
  902. const result = await that.service.base.select(selectBean, that.app.model.UsersMoneyLogs, '新用户福利查询失败,请稍候重试', true, true);
  903. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  904. } catch (err) {
  905. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  906. }
  907. }
  908. /**
  909. * [newUserBenefits 获取每日红包]
  910. * @return {[type]} [description]
  911. */
  912. async dayLucky() {
  913. const that = this;
  914. try {
  915. const data = await that.ctx.validate(that.luckyValidate, await that.ctx.anyParse());
  916. // 2023/11/17 用户今天是否已经抽奖
  917. const user = await that.useModel.findOne({
  918. where: { user_id: data.user_id },
  919. });
  920. const isTodayLucky = await that.service.baseUtils.isToday(user.lucky_time);
  921. // const isTodayLucky = false;
  922. if (isTodayLucky) {
  923. throw new Error('您今天已经抽奖了哦,明天再来吧!');
  924. } else {
  925. // 2023/11/17 : 发起概率抽奖 发放奖项 (红包上限)
  926. let prizes;
  927. if (user.money > 999) {
  928. prizes = [
  929. { reward: 6, weight: 1 },
  930. { reward: 1, weight: 95 },
  931. { reward: 10, weight: 1 },
  932. { reward: 8, weight: 1 },
  933. { reward: 30, weight: 1 },
  934. { reward: 20, weight: 1 }
  935. ];
  936. } else {
  937. prizes = [
  938. { reward: 6, weight: 4 },
  939. { reward: 1, weight: 1 },
  940. { reward: 10, weight: 50 },
  941. { reward: 8, weight: 10 },
  942. { reward: 30, weight: 5 },
  943. { reward: 20, weight: 30 }
  944. ];
  945. }
  946. const prizeAward = await that.lottery(prizes);
  947. // 2023/11/21 发放红包奖励
  948. // console.log(prizeAward);
  949. await that.service.shop.userMoneyAdd(user.user_id, prizeAward.reward, null, '每日抽奖红包', 0, 4);
  950. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', prizeAward, false));
  951. }
  952. } catch (err) {
  953. return that.ctx.appJson(that.app.szjcomo.appResult(err.message));
  954. }
  955. }
  956. //权重抽奖函数
  957. async lottery(prizes) {
  958. let total = 0;
  959. let percent;
  960. //下标标记数组
  961. let index = [];
  962. for (let i = 0; i < prizes.length; i++) {
  963. //判断元素的权重,为了实现小数权重,先将所有的值放大100倍
  964. percent = 'undefined' != typeof (prizes[i].weight) ? prizes[i].weight : 0;
  965. for (let j = 0; j < percent; j++) {
  966. index.push(i);
  967. }
  968. total += percent;
  969. }
  970. //随机数值
  971. let rand = Math.floor(Math.random() * total);
  972. return prizes[index[rand]];
  973. };
  974. /**
  975. * 更新用户信息
  976. * @date:2023/10/20
  977. */
  978. async updateUserInfo() {
  979. const that = this;
  980. try {
  981. const data = await that.ctx.validate(that.updateValidate, await that.ctx.anyParse());
  982. // 2023/10/20 更新用户信息
  983. const dataParam = {};
  984. if (data.is_office) {
  985. dataParam.is_office = true;
  986. }
  987. if (data.is_family) {
  988. dataParam.is_family = true;
  989. }
  990. dataParam.update_ttime = that.app.szjcomo.date('Y-m-d H:i:s');
  991. const updateBean = await that.app.comoBean.instance(dataParam, { where: { user_id: data.user_id } });
  992. const result = await that.service.base.update(updateBean, that.useModel, '微信用户信息更新失败,请重试');
  993. return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false));
  994. } catch (e) {
  995. return that.ctx.appJson(that.app.szjcomo.appResult(e.message));
  996. }
  997. }
  998. };