/* eslint-disable eqeqeq */ 'use strict'; const shopController = require('./shop.js'); const Decimal = require('decimal.js'); // 用户控制器 module.exports = class UserController extends shopController { // 使用模型 get useModel() { const that = this; return that.app.model.Users; } /** * [registerValidate 用户注册验证器] * @return {[type]} [description] */ get registerValidate() { const that = this; return { account_name: that.ctx.rules.name('账号') .required() .trim() .notEmpty(), password: that.ctx.rules.name('密码') .required() .trim() .notEmpty() .extend((field, value, row) => { row[field] = that.app.szjcomo.MD5(value); }), create_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s')) .required(), }; } /** * [wxRegisterAdnLoginValidate 微信登录和注册验证器] * @return {[type]} [description] */ get wxRegisterAdnLoginValidate() { const that = this; return { code: that.ctx.rules.name('微信授权code') .required() .notEmpty() .trim(), }; } /** * [loginValidate 用户登录] * @return {[type]} [description] */ get loginValidate() { const that = this; return { account_name: that.ctx.rules.name('用户账号') .required() .notEmpty() .trim(), password: that.ctx.rules.name('登录密码') .required() .notEmpty() .trim() .extend((field, value, row) => { row[field] = that.app.szjcomo.MD5(value); }), }; } /** * 更新用户信息 * @date:2023/10/20 */ get updateValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), }; } /** * [wxuserValidate 微信用户写入] * @return {[type]} [description] */ get wxuserValidate() { const that = this; return { nickname: that.ctx.rules.name('用户昵称') .required() .notEmpty() .trim(), openid: that.ctx.rules.name('openid') .required() .notEmpty() .trim(), password: that.ctx.rules.default(that.app.szjcomo.MD5('123456')) .required(), account_name: that.ctx.rules.default(`${that.app.szjcomo.str_rand(6)}${that.app.szjcomo.date('His')}`) .required(), money: that.ctx.rules.default(0) .number(), intergral: that.ctx.rules.default(0) .number(), headimgurl: that.ctx.rules.default('') .required(), login_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s')) .required(), city: that.ctx.rules.default('') .required(), province: that.ctx.rules.default('') .required(), country: that.ctx.rules.default('') .required(), sex: that.ctx.rules.default(0) .number(), subscribe: that.ctx.rules.default(0) .number(), unionid: that.ctx.rules.default('') .required(), create_time: that.ctx.rules.default(that.app.szjcomo.date('Y-m-d H:i:s')) .required(), }; } /** * [wxloginURLValidate 获取微信登录地址] * @return {[type]} [description] */ get wxloginURLValidate() { const that = this; return { page_uri: that.ctx.rules.name('当前地址') .required() .notEmpty() .trim(), }; } /** * [userMoneyValidate 获取用户余额] * @return {[type]} [description] */ get userMoneyValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), page: that.ctx.rules.default(1) .number(), limit: that.ctx.rules.default(50) .number(), }; } get luckyValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), }; } get userTransferValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), diningCoinCode: that.ctx.rules.default('') .required(), page: that.ctx.rules.default(1) .number(), limit: that.ctx.rules.default(50) .number(), }; } get businessCashCoinValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), }; } // 2023/1/13 提现验证器 get cashOutValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), cash_amount: that.ctx.rules.name('提现金额') .required() .notEmpty() .number(), remark: that.ctx.rules.name('备注说明') .default('') .trim(), }; } // 2023/2/28 餐币核销验证 get coinTransferValidate() { const that = this; return { user_id: that.ctx.rules.default(that.service.shop.getWebUserId()) .number(), coinAmount: that.ctx.rules.name('核销收取餐币') .required() .notEmpty() .number(), time: that.ctx.rules.name('餐币二维码刷新时间') .required() .notEmpty() .number(), diningCoinCode: that.ctx.rules.name('餐币二维码特征') .required() .notEmpty(), }; } /** * [login 用户登录 - 账号密码] * @return {[type]} [description] */ async login() { const that = this; try { const data = await that.ctx.validate(that.loginValidate, await that.ctx.postParse()); const user = await that.useModel.findOne({ where: { account_name: data.account_name, password: data.password }, // include: [ // { model: that.app.model.ProxyApplyLogs, as: 'proxyApplyLogs', attributes: [ 'verify_status' ] }, // ], attributes: [ 'user_id', 'account_name', 'nickname', 'headimgurl', 'openid', 'intergral', 'is_proxy', 'partner_id', 'is_office', 'is_family', 'lucky_time' ], raw: true, }); if (!user) throw new Error('登录失败,请检查账号密码是否正确'); user.isNew = false; const result = await that.loginHandle(user); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { console.log(err); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [loginHandle 登录处理] * @param {Object} user [description] * @return {[type]} [description] */ async loginHandle(user = {}) { const that = this; const token = that.app.jwt.sign(user, that.app.config.jwt.secret, { expiresIn: '24h' }); const result = { token, user }; return result; } /** * [register 用户注册] * @return {[type]} [description] */ async register() { const that = this; try { const data = await that.ctx.validate(that.registerValidate, await that.ctx.postParse()); const createBean = that.app.comoBean.instance(data, {}); const result = await that.service.manager.create(createBean, that.useModel, '注册失败,请稍候重试'); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [wxRegisterAdnLogin 用户登录 - 微信注册登录] * @return {[type]} [description] */ async wxRegisterAdnLogin() { const that = this; try { const appid = await that.service.configs.getConfigValue('wechat_account_appid'); const secret = await that.service.configs.getConfigValue('wechat_account_secret'); const data = await that.ctx.validate(that.wxRegisterAdnLoginValidate, await that.ctx.getParse()); const result = await that.service.wechat.authLogin(appid, secret, data.code); if (result.errcode) throw new Error(`微信通讯失败,错误信息:${result.errmsg}`); const userInfo = await that.service.wechat.authUserInfo(result); const user = await that.wxRegisterAdnLoginHandle(userInfo, data.inviteCode); const loginRes = await that.loginHandle(user); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', loginRes, false)); } catch (err) { await that.logs('user.js', err); return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [wxloginURL 微信登录地址] * @return {[type]} [description] */ async wxloginURL() { const that = this; try { const data = await that.ctx.validate(that.wxloginURLValidate, await that.ctx.postParse()); const appid = await that.service.configs.getConfigValue('wechat_account_appid'); const curarr = data.page_uri.split('#'); 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`; return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [wxRegisterAdnLoginHandle 注册并登录] * @param {Object} userInfo [description] * @param {Number} inviteCode * @return {[type]} [description] */ async wxRegisterAdnLoginHandle(userInfo = {}, inviteCode = -1) { const that = this; const inviter_id = inviteCode; // console.log('==========inviteCode========== : ' + inviter_id); const options = { where: { openid: userInfo.openid }, attributes: [ 'user_id', 'account_name', 'nickname', 'headimgurl', 'openid', 'partner_id', 'is_office', 'is_family', 'lucky_time' ], raw: true, }; let user = await that.useModel.findOne(options); if (!user) { // 2022/9/27 新用户注册 写入信息 user = await that.writeWxUser(userInfo); user.isNew = true; // 2022/9/27: 新用户红包奖励8.8元 await that.service.shop.userMoneyAdd(user.user_id, 8.8, null, '新用户注册奖励', 0, 1, -1); // 2022/9/29 受邀注册奖励 和 邀请新用户奖励 if (inviter_id > 0) { try { const inviterInfo = await that.useModel.findOne({ where: { user_id: inviter_id }, attributes: [ 'user_id', 'nickname', 'headimgurl' ], raw: true, }); await that.service.shop.userMoneyAdd(user.user_id, 1.68, null, '受邀注册奖励', 0, 2, inviter_id, inviterInfo.nickname, inviterInfo.headimgurl); await that.service.shop.userMoneyAdd(inviter_id, 1.68, null, '邀请新用户奖励', 0, 3, user.user_id, user.nickname, user.headimgurl); // 2022/11/17 邀请关系绑定 await that.service.inviter.addRelUserInviter(user.user_id, inviter_id); } catch (e) { // 邀请人id不存在 } } } else { user.isNew = false; // 2023/8/25 更新登录时间 const updateBean = await that.app.comoBean.instance({ login_time: that.app.szjcomo.date('Y-m-d H:i:s'), update_ttime: that.app.szjcomo.date('Y-m-d H:i:s'), unionid: userInfo.unionid, }, { where: { user_id: user.user_id } }); await that.service.base.update(updateBean, that.useModel, '微信用户登录时间更新失败,请重试'); } return user; } /** * [writeWxUser 写入微信用户] * @param {Object} userInfo [description] * @return {[type]} [description] */ async writeWxUser(userInfo = {}) { const that = this; const data = await that.ctx.validate(that.wxuserValidate, userInfo); const createBean = await that.app.comoBean.instance(data); const result = await that.service.base.create(createBean, that.useModel, '添加微信用户失败,请重试'); return { user_id: result.dataValues.user_id, account_name: result.dataValues.account_name, nickname: result.dataValues.nickname, headimgurl: result.dataValues.headimgurl, openid: result.dataValues.openid, unionid: result.dataValues.unionid, is_office: result.dataValues.is_office, is_family: result.dataValues.is_family, }; } /** * [userMoney 获取用户余额] * @return {[type]} [description] */ async userMoney() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const result = await that.service.shop.getUserMoney(data.user_id); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [userMoney 获取用户账户余额] * @return {[type]} [description] */ async userAccount() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const result = await that.service.shop.getUserAccount(data.user_id); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [userMoneyLog 用户资金明细] * @return {[type]} [description] */ async userMoneyLog() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { offset: (data.page - 1) * data.limit, limit: data.limit, where: { user_id: data.user_id }, order: [ [ 'log_id', 'desc' ] ], attributes: [ 'log_id', 'log_desc', 'change_time', 'money', 'type', 'inviter_id', 'inviter_name', 'inviter_img' ], }); const result = await that.service.base.select(selectBean, that.app.model.UsersMoneyLogs, '查询资金明细失败,请稍候重试', true, true); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [userMoneyLog 用户分佣明细] * @return {[type]} [description] */ async userCommissionLog() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { // offset: (data.page - 1) * data.limit, limit: data.limit, where: { user_id: data.user_id }, order: [ [ 'log_id', 'desc' ] ], attributes: [ 'log_desc', 'create_time', 'commission', 'type', 'inviter_id', 'inviter_name', 'inviter_img' ], }); // 2022/11/28 直接查询所有数据 不分页 const result = await that.service.base.select(selectBean, that.app.model.UsersCommissionLogs, '查询分佣明细失败,请稍候重试', false, true); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [userMoneyLog 用户餐币明细] * @return {[type]} [description] */ async coinDetail() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { where: { user_id: data.user_id }, order: [ [ 'log_id', 'desc' ] ], }); // 2022/11/28 直接查询所有数据 不分页 const result = await that.service.base.select(selectBean, that.app.model.DinnerCoinLogs, '查询分佣明细失败,请稍候重试', false, true); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } // 2023/2/28 用户餐饮币账户列表 async userDiningCoin() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const selectBean = await that.app.comoBean.instance(data, { where: { user_id: data.user_id, expired: false }, }); // 2023/2/28 直接查询所有数据 不分页 const result = await that.service.base.select(selectBean, that.app.model.DinnerCoins, '查询餐饮币账户失败,请稍候重试', false, true); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * 用户指定商家餐币账户余额 * @return {Promise<*>} */ async userCouldTransferCoin() { const that = this; try { const data = await that.ctx.validate(that.userTransferValidate, await that.ctx.getParse()); const res = await that.getCouldTransferCoin(data); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', { couldTransferCoins: res.account }, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * 用户可在指定商家消费的餐币账户余额 * @param data */ async getCouldTransferCoin(data) { const that = this; if (!data.diningCoinCode) { throw new Error('参数错误'); } const userInfo = await that.app.model.Users.findOne({ where: { openid: data.diningCoinCode }, attributes: [ 'user_id' ], }); if (!userInfo || userInfo.user_id < 0) { throw new Error('参数错误'); } const businessInfo = await that.app.model.Users.findOne({ where: { user_id: data.user_id }, attributes: [ 'partner_id' ], }); if (!businessInfo || businessInfo.partner_id < 1) { throw new Error('抱歉,您不是源森家具的合作商户,无权收款。'); } const seq = that.app.Sequelize; const selectBean = await that.app.comoBean.instance({}, { where: { user_id: userInfo.user_id, // partner_id: businessInfo.partner_id, // 2023/4/11 补充通用餐币 partner_id: { [seq.Op.in]: [ businessInfo.partner_id, 1 ] }, expired: false, }, attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ], }); const result = await that.service.base.select(selectBean, that.app.model.DinnerCoins, '查询餐饮币账户余额失败,请稍候重试', false, false); const res = JSON.parse(JSON.stringify(result)); res.customer_id = userInfo.user_id; res.partner_id = businessInfo.partner_id; res.business_id = data.user_id; return res; } /** * 商家可提现餐币金额 * @return {Promise<*>} */ async businessDiningCoinCouldCash() { const that = this; try { const data = await that.ctx.validate(that.businessCashCoinValidate, await that.ctx.getParse()); const res = await that.getDiningCoinCouldCash(data); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', res, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * 商家可提现餐币金额 * @param data */ async getDiningCoinCouldCash(data) { const that = this; const businessInfo = await that.app.model.Users.findOne({ where: { user_id: data.user_id }, }); if (!businessInfo || businessInfo.partner_id < 0) { throw new Error('您的账号还没有绑定的合作商铺哦'); } const partnerInfo = await that.app.model.PartnerInfo.findOne({ where: { id: businessInfo.partner_id }, }); if (!partnerInfo || partnerInfo.user_id !== data.user_id) { throw new Error('您的账号没有权限提现餐费哦'); } const seq = that.app.Sequelize; const currentDateTime = that.app.szjcomo.date('Y-m-d H:i:s'); const sevenDayTime = 24 * 60 * 60 * 1000; // 2023/3/1 24小时后可提现 const endTimeStamp = Date.parse(currentDateTime) - sevenDayTime; const endDateTime = that.app.szjcomo.date('Y-m-d H:i:s', endTimeStamp / 1000); // 2023/3/1 查询24小时前的所得餐币总和 以及所有时间提现的总和 再求和 即为 可提现餐币金额 // todo : 特殊情况 商家管理员有自家绑定的商铺 餐币消费支出 const selectBean = await that.app.comoBean.instance({}, { where: { user_id: businessInfo.user_id, partner_id: businessInfo.partner_id, create_time: { [seq.Op.lte]: endDateTime }, account: { [seq.Op.gte]: 0 }, }, attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ], }); const coinResult = await that.service.base.select(selectBean, that.app.model.DinnerCoinLogs, '查询商家可提现餐币金额失败,请稍候重试', false, false); const selectBean2 = await that.app.comoBean.instance({}, { where: { user_id: businessInfo.user_id, partner_id: businessInfo.partner_id, account: { [seq.Op.lte]: 0 }, type: { [seq.Op.ne]: -1 }, }, attributes: [ [ seq.fn('sum', seq.col('account')), 'account' ] ], }); const cashResult = await that.service.base.select(selectBean2, that.app.model.DinnerCoinLogs, '查询商家可提现餐币金额失败,请稍候重试', false, false); const coinRes = JSON.parse(JSON.stringify(coinResult)); const cashRes = JSON.parse(JSON.stringify(cashResult)); const couldCash = new Decimal(coinRes.account ? coinRes.account : 0) .add(new Decimal(cashRes.account ? cashRes.account : 0)) .toNumber(); coinRes.all_coin_could_cash = couldCash > 0 ? couldCash : 0; coinRes.all_cash = cashRes.account; coinRes.partner_id = businessInfo.partner_id; coinRes.partner_fees = partnerInfo.partner_fees; return coinRes; } /** * 商家申请核销餐币 * @return {Promise} */ async coinTransfer() { const that = this; const seq = that.app.Sequelize; const transaction = await that.app.model.transaction(); try { const data = await that.ctx.validate(that.coinTransferValidate, await that.ctx.postParse()); // 2023/2/28 获取可核销餐币 const intervalTime = Date.parse(that.app.szjcomo.date('Y-m-d H:i:s')) - data.time; if (intervalTime > 10 * 60 * 1000) { throw new Error('顾客付款码已超时失效,请重新扫码!'); } const transferParams = await that.getCouldTransferCoin(data); if (data.coinAmount > 0 && data.coinAmount <= transferParams.account) { // 2023/2/28 发起核销收取餐币 const partnerInfo = await that.app.model.PartnerInfo.findOne({ where: { id: transferParams.partner_id }, }); // 2023/4/12 核销餐币 包含通用电子餐币 const selectBean = await that.app.comoBean.instance({}, { where: { user_id: transferParams.customer_id, // partner_id: transferParams.partner_id, partner_id: { [seq.Op.in]: [ transferParams.partner_id, 1 ] }, expired: false, }, order: [ [ 'partner_id', 'desc' ], [ 'ori_partner', 'desc' ] ], }); // 2023/2/28 查询所有指定商家餐币 const result = await that.service.base.select(selectBean, that.app.model.DinnerCoins, '查询餐饮币账户失败,请稍候重试', false, true); const customerAccounts = []; let needAccount = 0; for (const resultElement of result) { needAccount += resultElement.account; customerAccounts.push(JSON.parse(JSON.stringify(resultElement))); if (needAccount >= data.coinAmount) { break; } } let firstCoinAmount; let secondCoinAmount; let thirdCoinAmount; let fourthCoinAmount; let incomeCoinAmount = 0; switch (customerAccounts.length) { case 1: firstCoinAmount = data.coinAmount; secondCoinAmount = 0; thirdCoinAmount = 0; fourthCoinAmount = 0; break; case 2: firstCoinAmount = customerAccounts[0].account; secondCoinAmount = data.coinAmount - firstCoinAmount; thirdCoinAmount = 0; fourthCoinAmount = 0; break; case 3: firstCoinAmount = customerAccounts[0].account; secondCoinAmount = customerAccounts[1].account; thirdCoinAmount = data.coinAmount - firstCoinAmount - secondCoinAmount; fourthCoinAmount = 0; break; case 4: firstCoinAmount = customerAccounts[0].account; secondCoinAmount = customerAccounts[1].account; thirdCoinAmount = customerAccounts[2].account; fourthCoinAmount = data.coinAmount - firstCoinAmount - secondCoinAmount - thirdCoinAmount; break; default: break; } // =========================================== 2023/3/1 第一个账户划账====================================== await that.doTransferCoins(that, transferParams, firstCoinAmount, partnerInfo, transaction, customerAccounts, 0); incomeCoinAmount += firstCoinAmount * (customerAccounts[0].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default); // =============================== 2023/3/1 需要第二个账户来继续划账 ========================================== if (customerAccounts.length > 1 && secondCoinAmount > 0) { await that.doTransferCoins(that, transferParams, secondCoinAmount, partnerInfo, transaction, customerAccounts, 1); incomeCoinAmount += secondCoinAmount * (customerAccounts[1].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default); } // =============================== 2023/3/18 需要第三个账户来继续划账 ========================================== if (customerAccounts.length > 2 && thirdCoinAmount > 0) { await that.doTransferCoins(that, transferParams, thirdCoinAmount, partnerInfo, transaction, customerAccounts, 2); incomeCoinAmount += thirdCoinAmount * (customerAccounts[2].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default); } // =============================== 2023/3/18 需要第四个账户来继续划账 ========================================== if (customerAccounts.length > 3 && fourthCoinAmount > 0) { await that.doTransferCoins(that, transferParams, fourthCoinAmount, partnerInfo, transaction, customerAccounts, 3); incomeCoinAmount += fourthCoinAmount * (customerAccounts[3].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default); } await transaction.commit(); incomeCoinAmount = incomeCoinAmount.toFixed(2); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', { incomeCoinAmount, transferCoinAmount: data.coinAmount, }, false)); } throw new Error('输入的核销金额有误,请稍后重试'); } catch (e) { if (transaction) transaction.rollback(); return that.ctx.appJson(that.app.szjcomo.appResult(e.message)); } } // 2023/3/1 餐币划账 具体逻辑和操作 async doTransferCoins(that, transferParams, coinAmount, partnerInfo, transaction, customerAccounts, index) { // 2023/3/6 获取顾客信息 const customerInfo = await that.app.model.Users.findOne({ where: { user_id: transferParams.customer_id }, transaction, raw: true, }); // 2023/3/1 扣去顾客餐币 await that.service.diningCoin.addDiningCoinChangeLog({ user_id: transferParams.customer_id, order_id: -1, partner_id: customerAccounts[index].partner_id, type: 3, account: -coinAmount, log_desc: partnerInfo.name + ' 消费', }, transaction); // 2023/3/1 更新顾客账户 const customerAccountBean = await that.app.comoBean.instance({ account: customerAccounts[index].account - coinAmount, update_time: that.app.szjcomo.date('Y-m-d H:i:s'), }, { where: { user_id: transferParams.customer_id, ori_partner: customerAccounts[index].ori_partner, partner_id: customerAccounts[index].partner_id, expired: false, }, transaction, }); if (customerAccounts[index].account === coinAmount) { // 2023/3/1 删除0元账户 await that.service.base.delete(customerAccountBean, that.app.model.DinnerCoins, '顾客餐饮币清零更新失败,请重试'); } else { await that.service.base.update(customerAccountBean, that.app.model.DinnerCoins, '顾客餐饮币余额更新失败,请重试'); } // 2023/3/1 划入商家账户记录 const businessCoinAmount = coinAmount * (customerAccounts[index].ori_partner ? partnerInfo.rate_from_partner : partnerInfo.rate_default); await that.service.diningCoin.addDiningCoinChangeLog({ user_id: transferParams.business_id, order_id: -1, partner_id: transferParams.partner_id, type: 4, account: businessCoinAmount, log_desc: '核销收取餐币', ori_partner: customerAccounts[index].ori_partner, customer_id: customerInfo.user_id, customer_name: customerInfo.nickname, customer_img: customerInfo.headimgurl, }, transaction); // 2023/2/28 查询商家餐饮币账户列表 添加 或 更新账户余额 const info = await that.app.model.DinnerCoins.findOne({ where: { user_id: transferParams.business_id, // ori_partner: customerAccounts[index].ori_partner, partner_id: transferParams.partner_id, expired: false, }, transaction, raw: true, }); if (!info) { // 2023/2/27 没有对应类型的餐饮币 则插入该类型的餐饮币账户 const createData = { user_id: transferParams.business_id, account: businessCoinAmount, // ori_partner: customerAccounts[index].ori_partner, ori_partner: true, partner_id: transferParams.partner_id, create_time: that.app.szjcomo.date('Y-m-d H:i:s'), expired: false, expired_time: that.app.szjcomo.date('Y-m-d H:i:s', parseInt(+new Date() + '') / 1000 + 90 * 24 * 60 * 60), }; createData.partner_name = partnerInfo.name; createData.partner_address = partnerInfo.address; createData.partner_tel = partnerInfo.tel_num; createData.partner_opening_time = partnerInfo.opening_time; const createBean = await that.app.comoBean.instance(createData, { transaction }); await that.service.base.create(createBean, that.app.model.DinnerCoins, '餐饮币发放失败,请重试'); } else { const updateBean = await that.app.comoBean.instance({ account: info.account + businessCoinAmount, update_time: that.app.szjcomo.date('Y-m-d H:i:s'), expired: false, expired_time: that.app.szjcomo.date('Y-m-d H:i:s', parseInt(+new Date() + '') / 1000 + 90 * 24 * 60 * 60), }, { where: { user_id: transferParams.business_id, // ori_partner: customerAccounts[index].ori_partner, partner_id: transferParams.partner_id, }, transaction, }); await that.service.base.update(updateBean, that.app.model.DinnerCoins, '餐饮币余额更新失败,请重试'); } } /** * 用户可提现金额(求和) * @return {Promise<*>} */ async userCommissionCouldCash() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const result = await that.getCouldCashCommission(data.user_id); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * 获取用户可提现金额(求和) * todo 判断订单状态 确认收货的订单才能提现 * @return {Promise} */ async getCouldCashCommission(user_id = -1) { if (user_id <= 0) { throw new Error('参数错误'); } const that = this; const seq = that.app.Sequelize; const currentDateTime = that.app.szjcomo.date('Y-m-d H:i:s'); const sevenDayTime = 7 * 24 * 60 * 60 * 1000; const endTimeStamp = Date.parse(currentDateTime) - sevenDayTime; const endDateTime = that.app.szjcomo.date('Y-m-d H:i:s', endTimeStamp / 1000); // 2022/11/28 查询7天前的所得分佣总和 以及所有时间得提现总和 再求和 即为 可提现金额 const selectBean = await that.app.comoBean.instance({ user_id }, { where: { user_id, create_time: { [seq.Op.lte]: endDateTime }, commission: { [seq.Op.gte]: 0 } }, attributes: [ 'user_id', [ seq.fn('sum', seq.col('commission')), 'all_commission' ] ], }); const commissionResult = await that.service.base.select(selectBean, that.app.model.UsersCommissionLogs, '查询佣金失败,请稍候重试', false, false); const selectBean2 = await that.app.comoBean.instance({ user_id }, { where: { user_id, commission: { [seq.Op.lte]: 0 }, type: { [seq.Op.ne]: -1 } }, // type-1提现失败类型 attributes: [ 'user_id', [ seq.fn('sum', seq.col('commission')), 'all_cash' ] ], }); const cashResult = await that.service.base.select(selectBean2, that.app.model.UsersCommissionLogs, '查询佣金失败,请稍候重试', false, false); const commRes = JSON.parse(JSON.stringify(commissionResult)); const cashRes = JSON.parse(JSON.stringify(cashResult)); commRes.all_commission_could_cash = new Decimal(commRes.all_commission ? commRes.all_commission : 0) .add(new Decimal(cashRes.all_cash ? cashRes.all_cash : 0)) .toNumber(); commRes.all_cash = cashRes.all_cash; return commRes; } /** * 用户佣金提现 * TODO 提现条件 分佣对应订单状态 确认收货 方可提现 * @return {Promise} */ async userCashOut() { const that = this; try { const data = await that.ctx.validate(that.cashOutValidate, await that.ctx.postParse()); // 2023/1/17 获取可提现佣金额度 const res = await that.getCouldCashCommission(data.user_id); if (data.cash_amount >= 0.1 && data.cash_amount <= res.all_commission_could_cash) { // 2023/1/31 发起提现 const result = await that.service.wxPay.transfer(data); // 2023/1/31 提现状态 if (result.status != 200) { throw new Error('提现转账出现异常,请联系客服后再重试'); } return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } throw new Error('输入提现金额有误,请稍后重试'); } catch (e) { return that.ctx.appJson(that.app.szjcomo.appResult(e.message)); } } /** * 用户佣金转电子餐费 * @return {Promise} */ async commission2DiningCoin() { // 2023/11/17 todo: 佣金转电子餐费 } /** * 商家餐币提现 * @return {Promise<*>} */ async userCoinCashOut() { const that = this; try { const data = await that.ctx.validate(that.cashOutValidate, await that.ctx.postParse()); // 2023/1/17 获取可提现佣金额度 const res = await that.getDiningCoinCouldCash(data); data.partner_id = res.partner_id; if (data.cash_amount >= 0.1 && data.cash_amount <= (res.all_coin_could_cash - res.partner_fees)) { // 2023/1/31 发起提现 const result = await that.service.businessPayService.transfer(data); // 2023/1/31 提现状态 if (result.status != 200) { throw new Error('提现转账出现异常,请联系客服后再重试'); } return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } throw new Error('输入提现金额有误,请稍后重试'); } catch (e) { return that.ctx.appJson(that.app.szjcomo.appResult(e.message)); } } /** * [newUserBenefits 查询新用户福利] * @return {[type]} [description] */ async newUserBenefits() { const that = this; try { const data = await that.ctx.validate(that.userMoneyValidate, await that.ctx.getParse()); const seq = that.app.Sequelize; const selectBean = await that.app.comoBean.instance(data, { offset: (data.page - 1) * data.limit, limit: data.limit, where: { user_id: data.user_id, type: { [seq.Op.in]: [ 1, 2 ] }, }, order: [ [ 'type', 'asc' ] ], }); const result = await that.service.base.select(selectBean, that.app.model.UsersMoneyLogs, '新用户福利查询失败,请稍候重试', true, true); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } /** * [newUserBenefits 获取每日红包] * @return {[type]} [description] */ async dayLucky() { const that = this; try { const data = await that.ctx.validate(that.luckyValidate, await that.ctx.anyParse()); // 2023/11/17 用户今天是否已经抽奖 const user = await that.useModel.findOne({ where: { user_id: data.user_id }, }); const isTodayLucky = await that.service.baseUtils.isToday(user.lucky_time); // const isTodayLucky = false; if (isTodayLucky) { throw new Error('您今天已经抽奖了哦,明天再来吧!'); } else { // 2023/11/17 : 发起概率抽奖 发放奖项 (红包上限) let prizes; if (user.money > 999) { prizes = [ { reward: 6, weight: 1 }, { reward: 1, weight: 95 }, { reward: 10, weight: 1 }, { reward: 8, weight: 1 }, { reward: 30, weight: 1 }, { reward: 20, weight: 1 } ]; } else { prizes = [ { reward: 6, weight: 4 }, { reward: 1, weight: 1 }, { reward: 10, weight: 50 }, { reward: 8, weight: 10 }, { reward: 30, weight: 5 }, { reward: 20, weight: 30 } ]; } const prizeAward = await that.lottery(prizes); // 2023/11/21 发放红包奖励 // console.log(prizeAward); await that.service.shop.userMoneyAdd(user.user_id, prizeAward.reward, null, '每日抽奖红包', 0, 4); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', prizeAward, false)); } } catch (err) { return that.ctx.appJson(that.app.szjcomo.appResult(err.message)); } } //权重抽奖函数 async lottery(prizes) { let total = 0; let percent; //下标标记数组 let index = []; for (let i = 0; i < prizes.length; i++) { //判断元素的权重,为了实现小数权重,先将所有的值放大100倍 percent = 'undefined' != typeof (prizes[i].weight) ? prizes[i].weight : 0; for (let j = 0; j < percent; j++) { index.push(i); } total += percent; } //随机数值 let rand = Math.floor(Math.random() * total); return prizes[index[rand]]; }; /** * 更新用户信息 * @date:2023/10/20 */ async updateUserInfo() { const that = this; try { const data = await that.ctx.validate(that.updateValidate, await that.ctx.anyParse()); // 2023/10/20 更新用户信息 const dataParam = {}; if (data.is_office) { dataParam.is_office = true; } if (data.is_family) { dataParam.is_family = true; } dataParam.update_ttime = that.app.szjcomo.date('Y-m-d H:i:s'); const updateBean = await that.app.comoBean.instance(dataParam, { where: { user_id: data.user_id } }); const result = await that.service.base.update(updateBean, that.useModel, '微信用户信息更新失败,请重试'); return that.ctx.appJson(that.app.szjcomo.appResult('SUCCESS', result, false)); } catch (e) { return that.ctx.appJson(that.app.szjcomo.appResult(e.message)); } } };