✨ feat(后台): 批量邮件
This commit is contained in:
@@ -13,7 +13,7 @@ import { taflush } from '../../../services/sdkService';
|
||||
import { ActivityInRemote } from '../../../domain/activityField/activityField';
|
||||
import { pushCurrentTime } from '../../../services/auctionService';
|
||||
import { SurveyModel, SurveyUpdate } from '../../../db/Survery';
|
||||
import { getSurvey } from '../../../services/gmService';
|
||||
import { compaireMailGoods, getSurvey } from '../../../services/gmService';
|
||||
import { sendMessageToAllWithSuc } from '../../../services/pushService';
|
||||
import { sendMailByContent, sendMailsByGmMail } from '../../../services/mailService';
|
||||
import { saveMemory } from '../../../services/log/memoryLogService';
|
||||
@@ -69,13 +69,28 @@ export class GmHandler {
|
||||
//对接gm后台,下发邮件
|
||||
async sendMail(gmmail: GMMailType, isPass: boolean, uid: number) {
|
||||
let id = gmmail._id;
|
||||
let { receivers, mailType, status, timeType, startTime, circleHour, circleDay } = gmmail;
|
||||
let { receivers, mailType, status, hasGoods, goods } = gmmail;
|
||||
if(status != GM_MAIL_STATUS.CREATE) {
|
||||
return resResult(STATUS.GM_MAIL_HAS_SENT);
|
||||
}
|
||||
|
||||
if(isPass) {
|
||||
await sendMailsByGmMail([gmmail]);
|
||||
let gmmailArr: GMMailType[] = [];
|
||||
if(mailType == GM_MAIL_TYPE.SINGLE && hasGoods && (!goods || goods.length == 0)) {
|
||||
for(let receiver of receivers) {
|
||||
let index = gmmailArr.findIndex(gmmail => compaireMailGoods(gmmail, receiver));
|
||||
if(index == -1) {
|
||||
gmmailArr.push({...gmmail, receivers: [receiver], goods: receiver.rewards, hasGoods: receiver?receiver.rewards.length > 0: false });
|
||||
} else {
|
||||
gmmailArr[index].receivers.push(receiver);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gmmailArr.push(gmmail);
|
||||
}
|
||||
|
||||
|
||||
await sendMailsByGmMail(gmmailArr);
|
||||
}
|
||||
await GMMailModel.updateMailById(id, { status: isPass? GM_MAIL_STATUS.PASS: GM_MAIL_STATUS.NOT_PASS, viewAt: new Date(), viewBy: uid }, uid);
|
||||
return resResult(STATUS.SUCCESS);
|
||||
|
||||
@@ -6,7 +6,7 @@ import { GroupMessageType } from "../db/GroupMessage";
|
||||
import { pinus } from "pinus";
|
||||
import { ServerlistModel, ServerlistType } from "../db/Serverlist";
|
||||
import { sendMailsByGmMail } from "./mailService";
|
||||
import GMMail, { GMMailModel } from '../db/GMMail';
|
||||
import GMMail, { GMMailModel, GMMailType, Receiver } from '../db/GMMail';
|
||||
import { getPastTime, nowSeconds } from "../pubUtils/timeUtil";
|
||||
import { CreateServerParam } from "../domain/backEndField/params";
|
||||
import { SignInData } from "../domain/activityField/signInField";
|
||||
@@ -385,4 +385,19 @@ export async function calHiddenData(uid: number) {
|
||||
}
|
||||
}
|
||||
await HiddenDataModel.updateHiddenData(getPastTime(), heroes, goods, uid);
|
||||
}
|
||||
|
||||
export function compaireMailGoods(gmmail: GMMailType, receiver: Receiver) {
|
||||
if(gmmail.hasGoods == false && (!receiver.rewards || receiver.rewards.length)) return true;
|
||||
let tmpGoods = gmmail.goods||[];
|
||||
let receiverGoods = receiver.rewards||[];
|
||||
|
||||
if(tmpGoods.length != receiverGoods.length) return false;
|
||||
tmpGoods.sort((a, b) => a.id - b.id);
|
||||
receiverGoods.sort((a, b) => a.id - b.id);
|
||||
for(let i = 0; i < tmpGoods.length; i++) {
|
||||
if(!receiverGoods[i]) return false;
|
||||
if(tmpGoods[i].id != receiverGoods[i].id || tmpGoods[i].count != receiverGoods[i].count) return false;
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -47,6 +47,16 @@ export default class MailController extends Controller {
|
||||
public async updateGMMail() {
|
||||
const { ctx } = this;
|
||||
const msg = ctx.request.body;
|
||||
if(msg.receivers && msg.receivers.length > 0) {
|
||||
try {
|
||||
for(let receiver of msg.receivers) {
|
||||
if(receiver.rewards) receiver.rewards = JSON.parse(receiver.rewards);
|
||||
}
|
||||
} catch(e) {
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS);
|
||||
}
|
||||
}
|
||||
|
||||
let params = new UpdateMailParams(msg);
|
||||
let check = params.checkParams();
|
||||
if(!check) return ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller } from 'egg';
|
||||
import { Controller, FileStream } from 'egg';
|
||||
import { Stream } from 'stream';
|
||||
import { STATUS } from '@consts';
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const unzip = require("unzip-stream");
|
||||
const temp = require('temp');
|
||||
const compressing = require("compressing");
|
||||
@@ -12,21 +12,25 @@ const pump = require('mz-modules/pump');
|
||||
const folderName = 'hot_update_files';
|
||||
const hotUpdateAddr = `/root/${folderName}`;
|
||||
const publishPath = '/root/hot_update_backup';
|
||||
import {exec} from 'child_process'
|
||||
import { reloadResources } from '@pubUtils/data';
|
||||
import { decodeArrayStr } from '@pubUtils/util';
|
||||
import { exec } from 'child_process'
|
||||
import { gameData, reloadResources } from '@pubUtils/data';
|
||||
import { decodeArrayStr, genCode, parseGoodStr } from '@pubUtils/util';
|
||||
import { getLocalImgUrl, getLocalQrCodeUrl, getRemoteImgUrl, getRemoteQrCodeUrl } from '@pubUtils/battleUtils';
|
||||
import { RewardInter } from '@pubUtils/interface';
|
||||
import { RoleModel } from '@db/Role';
|
||||
import { isNumber, isString } from 'util';
|
||||
const sendToWormhole = require('stream-wormhole');
|
||||
const XLSX = require('xlsx');
|
||||
|
||||
export default class UploadController extends Controller {
|
||||
|
||||
private deleteFolder (path) {
|
||||
private deleteFolder(path) {
|
||||
let files = [];
|
||||
if( fs.existsSync(path) ) {
|
||||
if (fs.existsSync(path)) {
|
||||
files = fs.readdirSync(path);
|
||||
files.forEach((file) =>{
|
||||
files.forEach((file) => {
|
||||
let curPath = path + "/" + file;
|
||||
if(fs.statSync(curPath).isDirectory()) {
|
||||
if (fs.statSync(curPath).isDirectory()) {
|
||||
this.deleteFolder(curPath);
|
||||
} else {
|
||||
fs.unlinkSync(curPath);
|
||||
@@ -37,303 +41,368 @@ export default class UploadController extends Controller {
|
||||
}
|
||||
|
||||
|
||||
private getFileList (p: string, resultArr: Array<{path:string,name:string}>) {
|
||||
console.log(p, fs.existsSync(p))
|
||||
if( fs.existsSync(p) ) {
|
||||
let files = fs.readdirSync(p);
|
||||
console.log(p, files, resultArr);
|
||||
files.forEach((file) =>{
|
||||
let curPath = p + "/" + file;
|
||||
// console.log(curPath, fs.statSync(curPath).isDirectory())
|
||||
if(fs.statSync(curPath).isDirectory()) {
|
||||
this.getFileList(curPath, resultArr);
|
||||
} else {
|
||||
resultArr.push({
|
||||
path: curPath,
|
||||
name: file
|
||||
});
|
||||
}
|
||||
});
|
||||
private getFileList(p: string, resultArr: Array<{ path: string, name: string }>) {
|
||||
console.log(p, fs.existsSync(p))
|
||||
if (fs.existsSync(p)) {
|
||||
let files = fs.readdirSync(p);
|
||||
console.log(p, files, resultArr);
|
||||
files.forEach((file) => {
|
||||
let curPath = p + "/" + file;
|
||||
// console.log(curPath, fs.statSync(curPath).isDirectory())
|
||||
if (fs.statSync(curPath).isDirectory()) {
|
||||
this.getFileList(curPath, resultArr);
|
||||
} else {
|
||||
resultArr.push({
|
||||
path: curPath,
|
||||
name: file
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async upload() {
|
||||
const { ctx } = this;
|
||||
const stream = await ctx.getFileStream();
|
||||
// const filename = stream.filename;
|
||||
// const target = path.join(url, filename);
|
||||
public async upload() {
|
||||
const { ctx } = this;
|
||||
const stream = await ctx.getFileStream();
|
||||
// const filename = stream.filename;
|
||||
// const target = path.join(url, filename);
|
||||
|
||||
// const writeStream = fs.createWriteStream(target);
|
||||
if (stream.mimeType == 'application/zip') {
|
||||
try {
|
||||
|
||||
let dirPath = await temp.mkdir(folderName); // 临时文件夹
|
||||
|
||||
// 解压上传文件的stream
|
||||
await this.doUnzipExtra(stream, dirPath);
|
||||
|
||||
console.log('读取文件');
|
||||
let files: any = fs.readdirSync(dirPath);
|
||||
let msg = '', result = '';
|
||||
for (let file of files) {
|
||||
let flag = false;
|
||||
|
||||
let arr = ['project.manifest', 'version.manifest', 'assets', 'src'];
|
||||
for (let fileName of arr) {
|
||||
console.log(`${dirPath}/${file}/${fileName}`);
|
||||
let result = fs.existsSync(`${dirPath}/${file}/${fileName}`);
|
||||
if (!result) {
|
||||
msg = '缺少文件' + fileName;
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
continue;
|
||||
} else {
|
||||
result = file;
|
||||
dirPath += `/${file}`;
|
||||
};
|
||||
}
|
||||
if (!result) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// 历史记录压缩移动
|
||||
console.log('历史记录压缩移动');
|
||||
let isEmpty = true;
|
||||
try {
|
||||
let oldFiles: any = fs.readdirSync(hotUpdateAddr);
|
||||
console.log(oldFiles.length);
|
||||
isEmpty = oldFiles.length <= 0;
|
||||
} catch (e) {
|
||||
isEmpty = true;
|
||||
}
|
||||
if (!isEmpty) {
|
||||
await compressing.zip.compressDir(hotUpdateAddr, `${dirPath}/${folderName}.zip`);
|
||||
if (!fs.existsSync(publishPath)) {
|
||||
fs.mkdirSync(publishPath);
|
||||
}
|
||||
let versionManifest: string = '';
|
||||
try {
|
||||
let version = JSON.parse(fs.readFileSync(`${hotUpdateAddr}/version.manifest`));
|
||||
versionManifest = version.version;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
fs.renameSync(`${dirPath}/${folderName}.zip`, `${publishPath}/${folderName}_${versionManifest}_${moment().format('YYMMDDHHmmss')}.zip`);
|
||||
|
||||
// 删除原始文件
|
||||
this.deleteFolder(hotUpdateAddr);
|
||||
}
|
||||
|
||||
// 保存新文件
|
||||
console.log('保存至热更新地址');
|
||||
fs.renameSync(dirPath, hotUpdateAddr);
|
||||
|
||||
temp.cleanupSync();
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR, null, err.message);
|
||||
}
|
||||
|
||||
} else {
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
private async doUnzipExtra(stream: Stream, dirPath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 解压上传文件的stream
|
||||
var unzipExtractor = unzip.Extract({ path: dirPath });
|
||||
unzipExtractor.on('close', function () {
|
||||
resolve(null);
|
||||
});
|
||||
|
||||
unzipExtractor.on('close', function (e) {
|
||||
reject(e);
|
||||
});
|
||||
|
||||
stream.pipe(unzipExtractor); // 异步写入文件
|
||||
})
|
||||
}
|
||||
|
||||
private jsonFolder = '/app/resource/jsons';
|
||||
private warjsonFolder = '/app/resource/warJsons';
|
||||
private tsFolder = '/app/pubUtils';
|
||||
|
||||
private distJsonFolder = `/../game-server/dist${this.jsonFolder}`;
|
||||
private distWarjsonFolder = `/../game-server/dist${this.warjsonFolder}`;
|
||||
private distTsFolder = `/../game-server/dist${this.tsFolder}`;
|
||||
|
||||
public async uploadJson() {
|
||||
const { ctx } = this;
|
||||
|
||||
|
||||
const parts = ctx.multipart({});
|
||||
const files = [];
|
||||
|
||||
try {
|
||||
let stream;
|
||||
let writeStream;
|
||||
|
||||
while ((stream = await parts()) != null) {
|
||||
console.log('******', stream);
|
||||
const filename = stream.filename;
|
||||
let filenameWithoutEx = filename ? filename.split('.')[0] : '';
|
||||
if (stream.mimeType == 'application/json') {
|
||||
if (filenameWithoutEx.match(/^\d{1,}$/)) {
|
||||
let target1 = path.join(this.config.baseDir, this.warjsonFolder, filename);
|
||||
let target2 = path.join(this.config.baseDir, this.distWarjsonFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
} else {
|
||||
let target1 = path.join(this.config.baseDir, this.jsonFolder, filename);
|
||||
let target2 = path.join(this.config.baseDir, this.distJsonFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
}
|
||||
} else if (stream.mimeType == 'application/octet-stream') {
|
||||
let target1 = path.join(this.config.baseDir, this.tsFolder, 'dicParam.js');
|
||||
let target2 = path.join(this.config.baseDir, this.distTsFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
}
|
||||
|
||||
let envs = decodeArrayStr(ctx.request.headers.env || '', ',');
|
||||
|
||||
if (envs.length > 0) { // 转发
|
||||
for (let env of envs) {
|
||||
if (env == ctx.app.config.realEnv) continue;
|
||||
await ctx.service.utils.transmit(env, ctx.request.url, writeStream);
|
||||
}
|
||||
}
|
||||
sendToWormhole(stream);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, {
|
||||
"files": files
|
||||
});
|
||||
|
||||
// console.log('****', stream);
|
||||
// // const filename = stream.filename;
|
||||
// // const target = path.join(url, filename);
|
||||
|
||||
// if(stream.mimeType == 'application/zip') {
|
||||
// try {
|
||||
// let dirPath = await temp.mkdir(this.jsonFolderName); // 临时文件夹
|
||||
|
||||
// console.log('读取文件');
|
||||
// await this.doUnzipExtra(stream, dirPath);
|
||||
// let files = new Array<{path:string,name:string}>();
|
||||
// this.getFileList(dirPath, files);
|
||||
// console.log(files);
|
||||
|
||||
// console.log('保存文件');
|
||||
// if (!fs.existsSync(this.jsonFolderPath)) {
|
||||
// fs.mkdirSync(this.jsonFolderPath);
|
||||
// }
|
||||
// for (let {path, name} of files) {
|
||||
// console.log(`${this.jsonFolderPath}/${name}`);
|
||||
// fs.renameSync(path, `${this.jsonFolderPath}/${name}`);
|
||||
// }
|
||||
|
||||
// temp.cleanupSync();
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, {
|
||||
// "files": JSON.stringify(files)
|
||||
// });
|
||||
// } catch (err) {
|
||||
// console.log(err)
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.INTERNAL_ERR);
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR);
|
||||
// }
|
||||
}
|
||||
|
||||
public async reloadResource() {
|
||||
const { ctx } = this;
|
||||
|
||||
// const writeStream = fs.createWriteStream(target);
|
||||
if(stream.mimeType == 'application/zip') {
|
||||
try {
|
||||
|
||||
let dirPath = await temp.mkdir(folderName); // 临时文件夹
|
||||
reloadResources();
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true });
|
||||
return;
|
||||
} catch (e) {
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 解压上传文件的stream
|
||||
await this.doUnzipExtra(stream, dirPath);
|
||||
public async uploadQrCode() {
|
||||
const { ctx } = this;
|
||||
|
||||
console.log('读取文件');
|
||||
let files:any = fs.readdirSync(dirPath);
|
||||
let msg = '', result = '';
|
||||
for(let file of files) {
|
||||
let flag = false;
|
||||
|
||||
let arr = ['project.manifest', 'version.manifest', 'assets', 'src'];
|
||||
for(let fileName of arr) {
|
||||
console.log(`${dirPath}/${file}/${fileName}`);
|
||||
let result = fs.existsSync(`${dirPath}/${file}/${fileName}`);
|
||||
if(!result) {
|
||||
msg = '缺少文件' + fileName;
|
||||
flag = true;
|
||||
try {
|
||||
const { ctx } = this;
|
||||
const parts = ctx.multipart();
|
||||
let part, env = ctx.request.headers.env, type = ctx.request.headers.type, fileName = '', writePath = '', ext = '', remoteUrl = '';
|
||||
while ((part = await parts()) != null) {
|
||||
if (part.length) {
|
||||
console.log('kv: ', `${part[0]}: ${part[1]}`);
|
||||
} else {
|
||||
if (!part.filename) continue;
|
||||
|
||||
let filenames = part.filename.split('.');
|
||||
ext = filenames[filenames.length - 1];
|
||||
if (type == 'popNotice' || type == 'loginPage' || type == 'loadingPage' || type == 'icon') {
|
||||
writePath = getLocalImgUrl(this.app.config.realEnv, type);
|
||||
fileName = `${moment().valueOf()}.${ext}`;
|
||||
remoteUrl = getRemoteImgUrl(this.app.config.realEnv, fileName, type);
|
||||
} else {
|
||||
writePath = getLocalQrCodeUrl(this.app.config.realEnv, env);
|
||||
fileName = `QR${moment().valueOf()}.${ext}`;
|
||||
remoteUrl = getRemoteQrCodeUrl(env, fileName);
|
||||
}
|
||||
|
||||
let fullPath = `${writePath}/${fileName}`;
|
||||
try {
|
||||
fs.accessSync(writePath);
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
fs.mkdirSync(writePath, { recursive: true });
|
||||
}
|
||||
}
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
fs.writeFileSync(fullPath, '');
|
||||
}
|
||||
const writeStream = fs.createWriteStream(fullPath);
|
||||
await pump(part, writeStream);
|
||||
|
||||
// remoteUrl = `${getRemoteRplUrl(ctx.app.config.realEnv, roleId, warType, battleCode)}/${battleCode}.bin`;
|
||||
}
|
||||
await sendToWormhole(part);
|
||||
}
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true, url: remoteUrl, ext });
|
||||
return;
|
||||
} catch (e) {
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadMailExcel() {
|
||||
const { ctx } = this;
|
||||
|
||||
try {
|
||||
const { ctx } = this;
|
||||
const stream = await ctx.getFileStream();
|
||||
let exceldata = await this.streamToExcekData(stream);
|
||||
|
||||
let result: { code: string, env: string, serverId: number, roleId: string, roleName: string, rewards: string, isError: boolean, isNameError: boolean, isRewardError: boolean }[] = [];
|
||||
for(let i = 1; i < exceldata.length; i++) {
|
||||
let data = exceldata[i];
|
||||
let newObj = { code: genCode(5), env: ctx.app.config.realEnv, serverId: data.serverId, roleId: data.roleId, roleName: '', rewards: '', isError: true, isNameError: true, isRewardError: true };
|
||||
if(data.roleId && isString(data.roleId)) {
|
||||
let role = await RoleModel.findByRoleId(data.roleId, 'roleName serverId');
|
||||
if(role && role.serverId == data.serverId) {
|
||||
newObj.roleName = role.roleName;
|
||||
newObj.serverId = role.serverId;
|
||||
newObj.isNameError = false;
|
||||
}
|
||||
}
|
||||
if(data.rewards && isString(data.rewards)) {
|
||||
try {
|
||||
let rewards: RewardInter[] = parseGoodStr(data.rewards);
|
||||
let isRewardError = false;
|
||||
for(let { id, count } of rewards) {
|
||||
let dicGoods = gameData.goods.get(id);
|
||||
if(!dicGoods) { isRewardError = true; break; }
|
||||
if(!isNumber(count) || count <= 0) { isRewardError = true; break; }
|
||||
}
|
||||
newObj.rewards = JSON.stringify(rewards);
|
||||
newObj.isRewardError = isRewardError;
|
||||
} catch(e) {
|
||||
console.log('updateMailExcel', e)
|
||||
}
|
||||
}
|
||||
newObj.isError = newObj.isNameError || newObj.isRewardError;
|
||||
result.push(newObj);
|
||||
}
|
||||
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { result });
|
||||
return;
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS, { result: [] });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async streamToExcekData(stream: FileStream): Promise<{ serverId: number, roleId: string, rewards: string }[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let exceldata = [];
|
||||
stream.on('error', reject);
|
||||
stream.on('data', function(chunk) {
|
||||
const workbook = XLSX.read(chunk);
|
||||
for(let sheet in workbook.Sheets) {
|
||||
if(workbook.Sheets.hasOwnProperty(sheet)) {
|
||||
exceldata = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(flag) {
|
||||
continue;
|
||||
} else {
|
||||
result = file;
|
||||
dirPath += `/${file}`;
|
||||
};
|
||||
}
|
||||
if(!result) {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
// 历史记录压缩移动
|
||||
console.log('历史记录压缩移动');
|
||||
let isEmpty = true;
|
||||
try {
|
||||
let oldFiles:any = fs.readdirSync(hotUpdateAddr);
|
||||
console.log(oldFiles.length);
|
||||
isEmpty = oldFiles.length <= 0;
|
||||
} catch(e) {
|
||||
isEmpty = true;
|
||||
}
|
||||
if(!isEmpty) {
|
||||
await compressing.zip.compressDir(hotUpdateAddr, `${dirPath}/${folderName}.zip`);
|
||||
if (!fs.existsSync(publishPath)) {
|
||||
fs.mkdirSync(publishPath);
|
||||
}
|
||||
let versionManifest: string = '';
|
||||
try {
|
||||
let version = JSON.parse(fs.readFileSync(`${hotUpdateAddr}/version.manifest`));
|
||||
versionManifest = version.version;
|
||||
}catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
fs.renameSync(`${dirPath}/${folderName}.zip`, `${publishPath}/${folderName}_${versionManifest}_${moment().format('YYMMDDHHmmss')}.zip`);
|
||||
|
||||
// 删除原始文件
|
||||
this.deleteFolder(hotUpdateAddr);
|
||||
}
|
||||
|
||||
// 保存新文件
|
||||
console.log('保存至热更新地址');
|
||||
fs.renameSync(dirPath, hotUpdateAddr);
|
||||
|
||||
temp.cleanupSync();
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR, null, err.message);
|
||||
}
|
||||
|
||||
} else {
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
private async doUnzipExtra(stream: Stream, dirPath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 解压上传文件的stream
|
||||
var unzipExtractor = unzip.Extract({ path: dirPath });
|
||||
unzipExtractor.on('close', function() {
|
||||
resolve(null);
|
||||
});
|
||||
stream.on('end', () => resolve(exceldata))
|
||||
});
|
||||
|
||||
unzipExtractor.on('close', function(e) {
|
||||
reject(e);
|
||||
});
|
||||
|
||||
stream.pipe(unzipExtractor); // 异步写入文件
|
||||
})
|
||||
}
|
||||
|
||||
private jsonFolder = '/app/resource/jsons';
|
||||
private warjsonFolder = '/app/resource/warJsons';
|
||||
private tsFolder = '/app/pubUtils';
|
||||
|
||||
private distJsonFolder = `/../game-server/dist${this.jsonFolder}`;
|
||||
private distWarjsonFolder = `/../game-server/dist${this.warjsonFolder}`;
|
||||
private distTsFolder = `/../game-server/dist${this.tsFolder}`;
|
||||
|
||||
public async uploadJson() {
|
||||
const { ctx } = this;
|
||||
|
||||
|
||||
const parts = ctx.multipart({ });
|
||||
const files = [];
|
||||
|
||||
try {
|
||||
let stream;
|
||||
let writeStream;
|
||||
|
||||
while ((stream = await parts()) != null) {
|
||||
console.log('******', stream);
|
||||
const filename = stream.filename;
|
||||
let filenameWithoutEx = filename?filename.split('.')[0]:'';
|
||||
if (stream.mimeType == 'application/json') {
|
||||
if (filenameWithoutEx.match(/^\d{1,}$/)) {
|
||||
let target1 = path.join(this.config.baseDir, this.warjsonFolder, filename);
|
||||
let target2 = path.join(this.config.baseDir, this.distWarjsonFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
} else {
|
||||
let target1 = path.join(this.config.baseDir, this.jsonFolder, filename);
|
||||
let target2 = path.join(this.config.baseDir, this.distJsonFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
}
|
||||
} else if (stream.mimeType == 'application/octet-stream') {
|
||||
let target1 = path.join(this.config.baseDir, this.tsFolder, 'dicParam.js');
|
||||
let target2 = path.join(this.config.baseDir, this.distTsFolder, filename);
|
||||
writeStream = fs.createWriteStream(target1);
|
||||
await pump(stream, writeStream);
|
||||
fs.copyFileSync(target1, target2);
|
||||
files.push(filename);
|
||||
}
|
||||
|
||||
let envs = decodeArrayStr(ctx.request.headers.env||'', ',');
|
||||
|
||||
if(envs.length > 0) { // 转发
|
||||
for(let env of envs) {
|
||||
if(env == ctx.app.config.realEnv) continue;
|
||||
await ctx.service.utils.transmit(env, ctx.request.url, writeStream);
|
||||
}
|
||||
}
|
||||
sendToWormhole(stream);
|
||||
}
|
||||
|
||||
}catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, {
|
||||
"files": files
|
||||
});
|
||||
|
||||
// console.log('****', stream);
|
||||
// // const filename = stream.filename;
|
||||
// // const target = path.join(url, filename);
|
||||
|
||||
// if(stream.mimeType == 'application/zip') {
|
||||
// try {
|
||||
// let dirPath = await temp.mkdir(this.jsonFolderName); // 临时文件夹
|
||||
|
||||
// console.log('读取文件');
|
||||
// await this.doUnzipExtra(stream, dirPath);
|
||||
// let files = new Array<{path:string,name:string}>();
|
||||
// this.getFileList(dirPath, files);
|
||||
// console.log(files);
|
||||
|
||||
// console.log('保存文件');
|
||||
// if (!fs.existsSync(this.jsonFolderPath)) {
|
||||
// fs.mkdirSync(this.jsonFolderPath);
|
||||
// }
|
||||
// for (let {path, name} of files) {
|
||||
// console.log(`${this.jsonFolderPath}/${name}`);
|
||||
// fs.renameSync(path, `${this.jsonFolderPath}/${name}`);
|
||||
// }
|
||||
|
||||
// temp.cleanupSync();
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, {
|
||||
// "files": JSON.stringify(files)
|
||||
// });
|
||||
// } catch (err) {
|
||||
// console.log(err)
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.INTERNAL_ERR);
|
||||
// }
|
||||
|
||||
// } else {
|
||||
// return ctx.body = ctx.service.utils.resResult(STATUS.GM_UPLOAD_FORMAT_ERR);
|
||||
// }
|
||||
}
|
||||
|
||||
public async reloadResource() {
|
||||
const { ctx } = this;
|
||||
|
||||
try {
|
||||
|
||||
reloadResources();
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true });
|
||||
return;
|
||||
} catch(e) {
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadQrCode() {
|
||||
const { ctx } = this;
|
||||
|
||||
try {
|
||||
const { ctx } = this;
|
||||
const parts = ctx.multipart();
|
||||
let part, env = ctx.request.headers.env, type = ctx.request.headers.type, fileName = '', writePath = '', ext = '', remoteUrl = '';
|
||||
while ((part = await parts()) != null) {
|
||||
if (part.length) {
|
||||
console.log('kv: ', `${part[0]}: ${part[1]}`);
|
||||
} else {
|
||||
if (!part.filename) continue;
|
||||
|
||||
let filenames = part.filename.split('.');
|
||||
ext = filenames[filenames.length - 1];
|
||||
if(type == 'popNotice'|| type == 'loginPage' || type == 'loadingPage' || type == 'icon') {
|
||||
writePath = getLocalImgUrl(this.app.config.realEnv, type);
|
||||
fileName = `${moment().valueOf()}.${ext}`;
|
||||
remoteUrl = getRemoteImgUrl(this.app.config.realEnv, fileName, type);
|
||||
} else {
|
||||
writePath = getLocalQrCodeUrl(this.app.config.realEnv, env);
|
||||
fileName = `QR${moment().valueOf()}.${ext}`;
|
||||
remoteUrl = getRemoteQrCodeUrl(env, fileName);
|
||||
}
|
||||
|
||||
let fullPath = `${writePath}/${fileName}`;
|
||||
try {
|
||||
fs.accessSync(writePath);
|
||||
} catch (err) {
|
||||
if (err) {
|
||||
fs.mkdirSync(writePath, { recursive: true });
|
||||
}
|
||||
}
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
fs.writeFileSync(fullPath, '');
|
||||
}
|
||||
const writeStream = fs.createWriteStream(fullPath);
|
||||
await pump(part, writeStream);
|
||||
|
||||
// remoteUrl = `${getRemoteRplUrl(ctx.app.config.realEnv, roleId, warType, battleCode)}/${battleCode}.bin`;
|
||||
}
|
||||
await sendToWormhole(part);
|
||||
}
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: true, url: remoteUrl, ext });
|
||||
return;
|
||||
} catch(e) {
|
||||
ctx.body = ctx.service.utils.resResult(STATUS.SUCCESS, { isOK: false, err: e.stack });
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function childExec(commond: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(commond, function(error, stdout, stderr){
|
||||
if(error) {
|
||||
exec(commond, function (error, stdout, stderr) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ export default (app: Application) => {
|
||||
router.post('/api/upload/uploadjson', tokenParser, controller.upload.uploadJson);
|
||||
router.post('/api/upload/reloadresource', tokenParser, controller.upload.reloadResource);
|
||||
router.post('/api/upload/uploadqrcode', tokenParser, controller.upload.uploadQrCode);
|
||||
router.post('/api/upload/uploadmailexcel', tokenParser, controller.upload.uploadMailExcel);
|
||||
|
||||
router.post('/api/gmaccount/getgmlist', tokenParser, controller.gmaccount.getGmList);
|
||||
router.post('/api/gmaccount/createaccount', tokenParser,controller.gmaccount.createGmAccount);
|
||||
|
||||
@@ -23,7 +23,7 @@ export default class Mail extends Service {
|
||||
role = await RoleModel.findByRoleId(roleId);
|
||||
}
|
||||
if(roleName) {
|
||||
role = await RoleModel.findByRoleName(roleName);
|
||||
role = await RoleModel.findByRoleName(roleName, serverId);
|
||||
}
|
||||
if(!role || role.serverId != serverId) return this.ctx.service.utils.resResult(STATUS.WRONG_PARMS);
|
||||
return this.ctx.service.utils.resResult(STATUS.SUCCESS, { env: this.ctx.app.config.realEnv, serverId, roleId: role.roleId, roleName: role.roleName })
|
||||
|
||||
@@ -47,9 +47,9 @@ export default (appInfo: EggAppInfo) => {
|
||||
fileSize: '100mb',
|
||||
mode: 'stream',
|
||||
whitelist: [
|
||||
'.json', '.ts', '.zip', '.jpg', '.png', '.jpeg', '.mp4', '.gif'
|
||||
'.json', '.ts', '.zip', '.jpg', '.png', '.jpeg', '.mp4', '.gif', '.xls', '.xlsx'
|
||||
],
|
||||
fileExtensions: ['.json', '.ts', '.zip', '.jpg', '.png', '.jpeg', '.mp4', '.gif'], // 扩展几种上传的文件格式
|
||||
fileExtensions: ['.json', '.ts', '.zip', '.jpg', '.png', '.jpeg', '.mp4', '.gif', '.xls', '.xlsx'], // 扩展几种上传的文件格式
|
||||
autoFields: true
|
||||
};
|
||||
|
||||
|
||||
@@ -36,10 +36,12 @@
|
||||
"egg-cors": "^2.2.3",
|
||||
"egg-http-proxy-middleware": "^1.0.3",
|
||||
"egg-scripts": "^2.6.0",
|
||||
"egg-xtransit": "^2.0.0",
|
||||
"exceljs": "^4.3.0",
|
||||
"form-data": "^4.0.0",
|
||||
"formstream": "^1.1.1",
|
||||
"moment": "^2.29.0",
|
||||
"node-xlsx": "^0.23.0",
|
||||
"pump": "^3.0.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"stream-wormhole": "^1.1.0",
|
||||
@@ -53,12 +55,12 @@
|
||||
"@types/supertest": "^2.0.0",
|
||||
"autod": "^3.0.1",
|
||||
"autod-egg": "^1.1.0",
|
||||
"egg-ci": "^1.8.0",
|
||||
"egg-bin": "^4.11.0",
|
||||
"egg-ci": "^1.8.0",
|
||||
"egg-mock": "^3.16.0",
|
||||
"tslib": "^1.9.0",
|
||||
"eslint": "^6.7.2",
|
||||
"eslint-config-egg": "^8.0.0",
|
||||
"tslib": "^1.9.0",
|
||||
"typescript": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { GMMail as StategyMail } from './ServerStategy';
|
||||
import { ServerlistType } from './Serverlist';
|
||||
import moment = require('moment');
|
||||
|
||||
class Reward {
|
||||
class MailReward {
|
||||
@prop({ required: true })
|
||||
id: number;
|
||||
@prop({ required: true })
|
||||
@@ -26,6 +26,8 @@ export class Receiver {
|
||||
roleId?: string;
|
||||
@prop({ required: true })
|
||||
roleName?: string;
|
||||
@prop({ required: true, type: MailReward, _id: false })
|
||||
rewards?: MailReward[];
|
||||
}
|
||||
|
||||
export default class GMMail extends BaseModel {
|
||||
@@ -33,8 +35,8 @@ export default class GMMail extends BaseModel {
|
||||
@prop({ required: false})
|
||||
hasGoods: boolean; // 有效时间,单位小时
|
||||
|
||||
@prop({ required: true, type: Reward, default: [], _id: false })
|
||||
goods: Reward[];
|
||||
@prop({ required: true, type: MailReward, default: [], _id: false })
|
||||
goods: MailReward[];
|
||||
|
||||
@prop({ required: true, default: true })
|
||||
timeType: MAIL_TIME_TYPE; // 邮件时间类型
|
||||
|
||||
@@ -404,8 +404,8 @@ export default class Role extends BaseModel {
|
||||
return role;
|
||||
}
|
||||
|
||||
public static async findByRoleName(roleName: string, select?: string, getters = false, virtuals = true) {
|
||||
const role: RoleType = await RoleModel.findOne({ roleName }).select(select).lean({ getters, virtuals });
|
||||
public static async findByRoleName(roleName: string, serverId: number, select?: string, getters = false, virtuals = true) {
|
||||
const role: RoleType = await RoleModel.findOne({ roleName, serverId }).select(select).lean({ getters, virtuals });
|
||||
return role;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,14 @@ export class UpdateMailParams {
|
||||
content: string;
|
||||
sendName: string;
|
||||
mailType: GM_MAIL_TYPE; // 收件人类型
|
||||
receivers: {env: string; serverId: number; roleId: string; roleName: string; }[];
|
||||
receivers: {env: string; serverId: number; roleId: string; roleName: string; rewards: {id: number; count: number}[] }[];
|
||||
reason: string; // 原因
|
||||
isSp: boolean = false; // 特殊邮件
|
||||
isSingle: boolean;
|
||||
|
||||
constructor(obj: UpdateMailParams) {
|
||||
this.goods = obj.goods;
|
||||
this.hasGoods = obj.goods?.length > 0;
|
||||
if(obj.goods && obj.goods?.length > 0) this.hasGoods = true;
|
||||
this.timeType = obj.timeType;
|
||||
this.expire = obj.expire;
|
||||
this.startTime = obj.startTime;
|
||||
@@ -44,6 +44,12 @@ export class UpdateMailParams {
|
||||
this.mailType = GM_MAIL_TYPE.SERVER;
|
||||
}
|
||||
this.receivers = obj.receivers;
|
||||
if(obj.receivers && obj.receivers.length > 0) {
|
||||
for(let { rewards } of obj.receivers) {
|
||||
console.log('#### rewards', rewards)
|
||||
if(rewards && rewards.length > 0) this.hasGoods = true;
|
||||
}
|
||||
}
|
||||
this.reason = obj.reason;
|
||||
this.isSp = obj.isSp;
|
||||
}
|
||||
|
||||
@@ -999,5 +999,12 @@
|
||||
"name": "上传二维码",
|
||||
"module": "server",
|
||||
"type": "update"
|
||||
},
|
||||
{
|
||||
"id": 144,
|
||||
"api": "/api/upload/uploadmailexcel",
|
||||
"name": "上传excel",
|
||||
"module": "server",
|
||||
"type": "update"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user