新增邮件发送和管理员功能
This commit is contained in:
@@ -2,23 +2,48 @@ import BizError from '../error/biz-error';
|
||||
import accountService from './account-service';
|
||||
import orm from '../entity/orm';
|
||||
import user from '../entity/user';
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { isDel } from '../const/entity-const';
|
||||
import { and, asc, count, desc, eq, inArray, like, sql } from 'drizzle-orm';
|
||||
import { emailConst, isDel, roleConst, userConst } from '../const/entity-const';
|
||||
import kvConst from '../const/kv-const';
|
||||
import KvConst from '../const/kv-const';
|
||||
import cryptoUtils from '../utils/crypto-utils';
|
||||
import emailService from './email-service';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
import dayjs from 'dayjs';
|
||||
import permService from './perm-service';
|
||||
import roleService from './role-service';
|
||||
import emailUtils from '../utils/email-utils';
|
||||
import saltHashUtils from '../utils/crypto-utils';
|
||||
import constant from '../const/constant';
|
||||
|
||||
const userService = {
|
||||
|
||||
async loginUserInfo(c, userId) {
|
||||
let user = await userService.selectById(c, userId);
|
||||
let account = await accountService.selectByEmailIncludeDel(c, user.email);
|
||||
delete user.password;
|
||||
delete user.salt;
|
||||
|
||||
const userRow = await userService.selectById(c, userId);
|
||||
|
||||
const [account, roleRow, permKeys] = await Promise.all([
|
||||
accountService.selectByEmailIncludeDel(c, userRow.email),
|
||||
roleService.selectById(c, userRow.type),
|
||||
userRow.email === c.env.admin ? Promise.resolve(['*']) : permService.userPermKeys(c, userId)
|
||||
]);
|
||||
|
||||
const user = {};
|
||||
user.userId = userRow.userId;
|
||||
user.sendCount = userRow.sendCount;
|
||||
user.email = userRow.email;
|
||||
user.accountId = account.accountId;
|
||||
user.type = c.env.admin === user.email ? 0 : 1;
|
||||
user.permKeys = permKeys;
|
||||
user.role = roleRow
|
||||
|
||||
if (c.env.admin === userRow.email) {
|
||||
user.role = constant.ADMIN_ROLE
|
||||
}
|
||||
|
||||
return user;
|
||||
},
|
||||
|
||||
|
||||
async resetPassword(c, params, userId) {
|
||||
|
||||
const { password } = params;
|
||||
@@ -57,10 +82,311 @@ const userService = {
|
||||
|
||||
async delete(c, userId) {
|
||||
await orm(c).update(user).set({ isDel: isDel.DELETE }).where(eq(user.userId, userId)).run();
|
||||
await Promise.all([
|
||||
c.env.kv.delete(kvConst.AUTH_INFO + userId),
|
||||
accountService.removeByUserId(c, userId)
|
||||
await c.env.kv.delete(kvConst.AUTH_INFO + userId)
|
||||
},
|
||||
|
||||
|
||||
async physicsDeleteAll(c) {
|
||||
const userIdsRow = await orm(c).select().from(user).where(eq(user.isDel, isDel.DELETE)).limit(99);
|
||||
if (userIdsRow.length === 0) {
|
||||
return;
|
||||
}
|
||||
const userIds = userIdsRow.map(item => item.userId);
|
||||
await accountService.physicsDeleteByUserIds(c, userIds);
|
||||
await orm(c).delete(user).where(inArray(user.userId, userIds)).run();
|
||||
if (userIdsRow.length === 99) {
|
||||
await this.physicsDeleteAll(c);
|
||||
}
|
||||
},
|
||||
|
||||
async physicsDelete(c, params) {
|
||||
const { userId } = params
|
||||
await accountService.physicsDeleteByUserIds(c, [userId])
|
||||
await orm(c).delete(user).where(eq(user.userId, userId)).run();
|
||||
},
|
||||
|
||||
async list(c, params, userId) {
|
||||
|
||||
let { num, size, email, timeSort, status } = params;
|
||||
|
||||
size = Number(size);
|
||||
num = Number(num);
|
||||
timeSort = Number(timeSort);
|
||||
params.isDel = Number(params.isDel);
|
||||
if (size > 50) {
|
||||
size = 50;
|
||||
}
|
||||
|
||||
num = (num - 1) * size;
|
||||
|
||||
const conditions = [];
|
||||
|
||||
if (status > -1) {
|
||||
conditions.push(eq(user.status, status));
|
||||
conditions.push(eq(user.isDel, isDel.NORMAL));
|
||||
}
|
||||
|
||||
|
||||
if (email) {
|
||||
conditions.push(like(user.email, `${email}%`));
|
||||
}
|
||||
|
||||
if (params.isDel) {
|
||||
conditions.push(eq(user.isDel, params.isDel));
|
||||
}
|
||||
|
||||
|
||||
const query = orm(c).select().from(user)
|
||||
.where(and(...conditions));
|
||||
|
||||
|
||||
if (timeSort) {
|
||||
query.orderBy(asc(user.userId));
|
||||
} else {
|
||||
query.orderBy(desc(user.userId));
|
||||
}
|
||||
|
||||
const list = await query.limit(size).offset(num);
|
||||
|
||||
const { total } = await orm(c)
|
||||
.select({ total: count() })
|
||||
.from(user)
|
||||
.where(and(...conditions)).get();
|
||||
const userIds = list.map(user => user.userId);
|
||||
|
||||
const types = [...new Set(list.map(user => user.type))];
|
||||
|
||||
const [emailCounts, delEmailCounts, sendCounts, delSendCounts, accountCounts, delAccountCounts, roleList] = await Promise.all([
|
||||
emailService.selectUserEmailCountList(c, userIds, emailConst.type.RECEIVE),
|
||||
emailService.selectUserEmailCountList(c, userIds, emailConst.type.RECEIVE, isDel.DELETE),
|
||||
emailService.selectUserEmailCountList(c, userIds, emailConst.type.SEND),
|
||||
emailService.selectUserEmailCountList(c, userIds, emailConst.type.SEND, isDel.DELETE),
|
||||
accountService.selectUserAccountCountList(c, userIds),
|
||||
accountService.selectUserAccountCountList(c, userIds, isDel.DELETE),
|
||||
roleService.selectByIdsHasPermKey(c, types,'email:send')
|
||||
]);
|
||||
|
||||
const receiveMap = Object.fromEntries(emailCounts.map(item => [item.userId, item.count]));
|
||||
const sendMap = Object.fromEntries(sendCounts.map(item => [item.userId, item.count]));
|
||||
const accountMap = Object.fromEntries(accountCounts.map(item => [item.userId, item.count]));
|
||||
|
||||
const delReceiveMap = Object.fromEntries(delEmailCounts.map(item => [item.userId, item.count]));
|
||||
const delSendMap = Object.fromEntries(delSendCounts.map(item => [item.userId, item.count]));
|
||||
const delAccountMap = Object.fromEntries(delAccountCounts.map(item => [item.userId, item.count]));
|
||||
|
||||
for (const user of list) {
|
||||
|
||||
const userId = user.userId;
|
||||
|
||||
user.receiveEmailCount = receiveMap[userId] || 0;
|
||||
user.sendEmailCount = sendMap[userId] || 0;
|
||||
user.accountCount = accountMap[userId] || 0;
|
||||
|
||||
user.delReceiveEmailCount = delReceiveMap[userId] || 0;
|
||||
user.delSendEmailCount = delSendMap[userId] || 0;
|
||||
user.delAccountCount = delAccountMap[userId] || 0;
|
||||
|
||||
const roleIndex = roleList.findIndex(roleRow => user.type === roleRow.roleId);
|
||||
let sendAction = {};
|
||||
|
||||
if (roleIndex > -1) {
|
||||
sendAction.sendType = roleList[roleIndex].sendType;
|
||||
sendAction.sendCount = roleList[roleIndex].sendCount;
|
||||
sendAction.hasPerm = true;
|
||||
} else {
|
||||
sendAction.hasPerm = false;
|
||||
}
|
||||
|
||||
if (user.email === c.env.admin) {
|
||||
sendAction.sendType = constant.ADMIN_ROLE.sendType;
|
||||
sendAction.sendCount = constant.ADMIN_ROLE.sendCount;
|
||||
sendAction.hasPerm = true;
|
||||
user.type = 0
|
||||
}
|
||||
|
||||
user.sendAction = sendAction;
|
||||
}
|
||||
|
||||
return { list, total };
|
||||
},
|
||||
|
||||
async updateUserInfo(c, userId, recordCreateIp = false) {
|
||||
|
||||
const ua = c.req.header('user-agent') || '';
|
||||
console.log(ua);
|
||||
const parser = new UAParser(ua);
|
||||
const { browser, device, os } = parser.getResult();
|
||||
|
||||
let browserInfo = null;
|
||||
let osInfo = null;
|
||||
|
||||
if (browser.name) {
|
||||
browserInfo = browser.name + ' ' + browser.version;
|
||||
}
|
||||
|
||||
if (os.name) {
|
||||
osInfo = os.name + os.version;
|
||||
}
|
||||
|
||||
let deviceInfo = 'Desktop';
|
||||
|
||||
const hasVendor = !!device?.vendor;
|
||||
const hasModel = !!device?.model;
|
||||
|
||||
if (hasVendor || hasModel) {
|
||||
const vendor = device.vendor || '';
|
||||
const model = device.model || '';
|
||||
const type = device.type || '';
|
||||
|
||||
const namePart = [vendor, model].filter(Boolean).join(' ');
|
||||
const typePart = type ? ` (${type})` : '';
|
||||
deviceInfo = (namePart + typePart).trim();
|
||||
}
|
||||
|
||||
const userIp = c.req.header('cf-connecting-ip') || '';
|
||||
|
||||
const params = {
|
||||
os: osInfo,
|
||||
browser: browserInfo,
|
||||
device: deviceInfo,
|
||||
activeIp: userIp,
|
||||
activeTime: dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
};
|
||||
|
||||
if (recordCreateIp) {
|
||||
params.createIp = userIp;
|
||||
}
|
||||
|
||||
await orm(c)
|
||||
.update(user)
|
||||
.set(params)
|
||||
.where(eq(user.userId, userId))
|
||||
.run();
|
||||
},
|
||||
|
||||
async setPwd(c, params) {
|
||||
|
||||
const { password, userId } = params;
|
||||
await this.resetPassword(c, { password }, userId);
|
||||
},
|
||||
|
||||
async setStatus(c, params) {
|
||||
|
||||
const { status, userId } = params;
|
||||
|
||||
await orm(c)
|
||||
.update(user)
|
||||
.set({ status })
|
||||
.where(eq(user.userId, userId))
|
||||
.run();
|
||||
|
||||
if (status === userConst.status.BAN) {
|
||||
c.env.kv.delete(KvConst.AUTH_INFO + userId);
|
||||
}
|
||||
},
|
||||
|
||||
async setType(c, params) {
|
||||
|
||||
const { type, userId } = params;
|
||||
|
||||
const roleRow = await roleService.selectById(c, type);
|
||||
|
||||
if (!roleRow) {
|
||||
throw new BizError('身份不存在');
|
||||
}
|
||||
|
||||
await orm(c)
|
||||
.update(user)
|
||||
.set({ type })
|
||||
.where(eq(user.userId, userId))
|
||||
.run();
|
||||
|
||||
if (type) {
|
||||
const authInfo = await c.env.kv.get(KvConst.AUTH_INFO + userId, { type: 'json' });
|
||||
if (authInfo) {
|
||||
authInfo.user.type = type;
|
||||
await c.env.kv.put(KvConst.AUTH_INFO + userId, JSON.stringify(authInfo));
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
async incrUserService(c, quantity, userId) {
|
||||
await orm(c).update(user).set({
|
||||
sendCount: sql`${user.sendCount}
|
||||
+
|
||||
${quantity}`
|
||||
}).where(eq(user.userId, userId)).run();
|
||||
},
|
||||
|
||||
async updateAllUserType(c, type, curType) {
|
||||
await orm(c)
|
||||
.update(user)
|
||||
.set({ type })
|
||||
.where(eq(user.type, curType))
|
||||
.run();
|
||||
},
|
||||
|
||||
async add(c, params) {
|
||||
|
||||
const { email, type, password } = params;
|
||||
|
||||
if (!c.env.domain.includes(emailUtils.getDomain(email))) {
|
||||
throw new BizError('非法邮箱域名');
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
throw new BizError('密码必须大于6位');
|
||||
}
|
||||
|
||||
const accountRow = await accountService.selectByEmailIncludeDel(c, email);
|
||||
|
||||
if (accountRow && accountRow.isDel === isDel.DELETE) {
|
||||
throw new BizError('该邮箱已被注销');
|
||||
}
|
||||
|
||||
if (accountRow) {
|
||||
throw new BizError('该邮箱已被注册');
|
||||
}
|
||||
|
||||
const role = roleService.selectById(c, type);
|
||||
|
||||
if (!role) {
|
||||
throw new BizError('权限身份不存在');
|
||||
}
|
||||
|
||||
const { salt, hash } = await saltHashUtils.hashPassword(password);
|
||||
|
||||
const userId = await userService.insert(c, { email, password: hash, salt, type });
|
||||
|
||||
await accountService.insert(c, { userId: userId, email, type });
|
||||
},
|
||||
|
||||
async resetDaySendCount(c) {
|
||||
const roleList = await roleService.selectByIdsAndSendType(c, 'email:send', roleConst.sendType.DAY);
|
||||
const roleIds = roleList.map(action => action.roleId);
|
||||
await orm(c).update(user).set({ sendCount: 0 }).where(inArray(user.type, roleIds)).run();
|
||||
},
|
||||
|
||||
async resetSendCount(c, params) {
|
||||
await orm(c).update(user).set({ sendCount: 0 }).where(eq(user.userId, params.userId)).run();
|
||||
},
|
||||
|
||||
async restore(c, params) {
|
||||
const { userId, type } = params
|
||||
await orm(c)
|
||||
.update(user)
|
||||
.set({ isDel: isDel.NORMAL })
|
||||
.where(eq(user.userId, userId))
|
||||
.run();
|
||||
const userRow = await this.selectById(c, userId);
|
||||
await accountService.restoreByEmail(c, userRow.email);
|
||||
|
||||
if (type) {
|
||||
await emailService.restoreByUserId(c, userId);
|
||||
await accountService.restoreByUserId(c, userId);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user