diff --git a/game-server/app/servers/role/handler/itemHandler.ts b/game-server/app/servers/role/handler/itemHandler.ts index d027c1009..0b10dfc95 100644 --- a/game-server/app/servers/role/handler/itemHandler.ts +++ b/game-server/app/servers/role/handler/itemHandler.ts @@ -19,6 +19,9 @@ import { checkTaskWithHero, checkTaskWithEquip, checkTask, checkTaskWithArgs, ch import { useGiftPackage } from "../../../services/activity/giftPackageService"; import { getAp, setAp, setApBuyTimes } from "../../../services/actionPointService"; import { ActionPointModel } from "../../../db/ActionPoint"; +import { GiftCodeDetailModel } from "../../../db/GiftCodeDetail"; +import { GiftCodeType, GiftCodeModel } from "../../../db/GiftCode"; +import UserGiftCode, { UserGiftCodeModel } from "../../../db/UserGiftCode"; export default function (app: Application) { return new ItemHandler(app); @@ -141,6 +144,35 @@ export class ItemHandler { }); } + // 兑换礼包码 + public async useGiftCode(msg: { code: string }, session: BackendSession) { + const roleId: string = session.get('roleId'); + const roleName: string = session.get('roleName'); + const sid: string = session.get('sid'); + const { code } = msg; + + let giftCodeDetail = await GiftCodeDetailModel.findByCode(code); + let giftCode = giftCodeDetail.giftCode; + + if(giftCode.isLimit && giftCodeDetail.usedNum >= giftCode.count) { + return resResult(STATUS.GIFT_CODE_USED_NUM_MAX); + } + if(giftCode.beginTime > new Date()) return resResult(STATUS.GIFT_CODE_NOT_START); + if(giftCode.endTime < new Date()) return resResult(STATUS.GIFT_CODE_HAS_EXPIRED); + + let userGiftCode = await UserGiftCodeModel.findByCode(roleId, code); + if(userGiftCode) { + return resResult(STATUS.YOU_HAVE_USED_THIS_CODE); + } + + await UserGiftCode.createCode(roleId, code, giftCode); + await GiftCodeDetailModel.increaseUsedNum(code); + await GiftCodeModel.increaseUsedNum(giftCode.id); + + let goods = await addItems(roleId, roleName, sid, giftCode.goods); + return resResult(STATUS.SUCCESS, { goods }); + } + public async debugIncAp(msg: { magicWord: string, ap: number }, session: BackendSession) { const { magicWord } = msg; if (magicWord !== DEBUG_MAGIC_WORD) { diff --git a/gm-server/app/controller/users.ts b/gm-server/app/controller/users.ts index 726171e0f..1085b3f32 100644 --- a/gm-server/app/controller/users.ts +++ b/gm-server/app/controller/users.ts @@ -127,4 +127,44 @@ export default class UserController extends Controller { const { selectedRowKeys: roleIdAndIds, count } = ctx.request.body; ctx.body = await ctx.service.users.setItemCount(roleIdAndIds, count); } + + public async getGiftCodeList() { + const { ctx } = this; + const { page, pageSize, sortField, sortOrder, form } = ctx.request.body; + ctx.body = await ctx.service.users.getGiftCodeList(page, pageSize, sortField, sortOrder, form); + } + + public async updateGiftCode() { + const { ctx } = this; + const { id, values } = ctx.request.body; + ctx.body = await ctx.service.users.updateGiftCode(id, values); + } + + public async generateGiftCode() { + const { ctx } = this; + const { id, generateType, code, generateNum, codeLen } = ctx.request.body; + if(generateType == 1) { + if(!generateNum) { + ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS); + return + } + } else if (generateType == 2) { + if(!code) { + ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS); + return + } + } else { + ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS); + return + } + + ctx.body = await ctx.service.users.generateGiftCode(id, generateType, code, generateNum, codeLen); + } + + public async getGiftCodeDetails() { + const { ctx } = this; + const { id } = ctx.params; + ctx.body = await ctx.service.users.getGiftCodeDetails(parseInt(id)); + return + } } diff --git a/gm-server/app/router.ts b/gm-server/app/router.ts index de39c4e21..989930e17 100644 --- a/gm-server/app/router.ts +++ b/gm-server/app/router.ts @@ -42,6 +42,11 @@ export default (app: Application) => { router.post('/api/users/deleteequip', tokenParser, controller.users.deleteEquip); router.post('/api/users/deleteitem', tokenParser, controller.users.deleteItem); router.post('/api/users/setitemcount', tokenParser, controller.users.setItemCount); + router.post('/api/users/getgiftcodelist', controller.users.getGiftCodeList); + router.post('/api/users/updategiftcode', controller.users.updateGiftCode); + router.post('/api/users/generategiftcode', controller.users.generateGiftCode); + // router.post('/api/users/delgiftCode', controller.users.delGiftCode); + router.get('/api/users/getgiftcodedetails/:id/:filename', controller.users.getGiftCodeDetails); router.post('/api/game/getserverenv', tokenParser, controller.game.getServerEnv); router.post('/api/game/getserverlistbyenv', tokenParser, controller.game.getServerListByEnv); diff --git a/gm-server/app/service/users.ts b/gm-server/app/service/users.ts index 6e39f169a..161efe115 100644 --- a/gm-server/app/service/users.ts +++ b/gm-server/app/service/users.ts @@ -33,6 +33,10 @@ import { FriendShipModel } from '@db/FriendShip'; import { FriendApplyModel } from '@db/FriendApply'; import { FriendRelationModel } from '@db/FriendRelation'; import { createHero as pubCreateHero, addSkin } from '@pubUtils/itemUtils'; +import { GiftCodeModel } from '@db/GiftCode'; +import { GiftCodeDetailModel } from '@db/GiftCodeDetail'; + +// import * as fs from 'fs'; /** * Test Service @@ -657,4 +661,76 @@ export default class GMUsers extends Service { return ctx.service.utils.resResult(STATUS.INTERNAL_ERR); } } + + public async getGiftCodeList(page: number, pageSize: number, sortField: string, sortOrder: string, form: { name?: string, current?: boolean, } = {}) { + const { ctx } = this; + + const list = await GiftCodeModel.findByCondition(page, pageSize, sortField, sortOrder, form); + const total = await GiftCodeModel.countByCondition( form ) + return ctx.service.utils.resResult(STATUS.SUCCESS, { + list: list.map(cur => { + return { ...cur, beginTime: cur.beginTime.getTime(), endTime: cur.endTime.getTime() } + }), total + }); + } + + public async updateGiftCode(id: string|number, params: any) { + const { ctx } = this; + if(params.beginTime) params.beginTime = new Date(params.beginTime); + if(params.endTime) params.endTime = new Date(params.endTime); + + try{ + params.goods = JSON.parse(params.goods); + } catch(e) { + console.error(e); + return ctx.body = ctx.service.utils.resResult(STATUS.WRONG_PARMS); + } + const result = await GiftCodeModel.updateData(id, params, ctx.user?.uid); + if(!result) return ctx.service.utils.resResult(STATUS.INTERNAL_ERR); + return ctx.service.utils.resResult(STATUS.SUCCESS); + } + + public async generateGiftCode(id: number, generateType: 1|2, code: string, generateNum: number, codeLen: number = 8) { + const { ctx } = this; + + let giftCode = await GiftCodeModel.findData(id); + if(!giftCode) return ctx.service.utils.resResult(STATUS.WRONG_PARMS); + + let length = 0, isLimit = false, count = 0, generateCode = ''; + if(generateType == 1) { + let generateResult = await GiftCodeDetailModel.generateMany(giftCode, generateNum, codeLen, ctx.user?.uid); + length = generateResult.length; + isLimit = true; + count = 1; + } else if (generateType == 2) { + let generateResult = await GiftCodeDetailModel.generateOne(giftCode, code, ctx.user?.uid); + length = generateResult.length; + generateCode = code; + } + if(length <= 0) return ctx.service.utils.resResult(STATUS.INTERNAL_ERR); + giftCode = await GiftCodeModel.updateData(id, { generateCnt: giftCode.generateCnt + length, generateType, isLimit, count, generateCode }, ctx.user?.uid); + return ctx.service.utils.resResult(STATUS.SUCCESS, { giftCode }) + } + + public async getGiftCodeDetails(id: number) { + const { ctx } = this; + + let giftCode = await GiftCodeModel.findData(id); + if(!giftCode) return ctx.service.utils.resResult(STATUS.WRONG_PARMS); + let { generateCnt } = giftCode; + let limit = 1000; + let n = Math.ceil(generateCnt / limit); + let codes: string[] = []; + for(let i = 0; i < n; i++) { + let giftCodeDetails = await GiftCodeDetailModel.findByGiftCode(giftCode); + for(let {code} of giftCodeDetails) { + codes.push(code); + } + } + console.log(id, codes.join()) + + ctx.set('Content-Type', 'application/octet-stream'); + + return Buffer.from(codes.join('\n')); + } } diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 5d3569750..a05e8cef4 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -40,7 +40,8 @@ export const COUNTER = { SERVER_BY_TYPE: { name: 'serverby', def: 1 }, SERVER: { name: 'server', def: 1 }, ACTIVITY_GROUP_TYPE: { name: 'actgrptype', def: 1 }, - NOTICE: { name: 'notice', def: 1 } + NOTICE: { name: 'notice', def: 1 }, + GIFT_CODE: { name: 'giftCode', def: 1 }, }; export const DEFAULT_HEROES = [19, 53, 46, 40, 22, 56, 32, 28, 18]; diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 43a2ef758..d84a40098 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -330,10 +330,17 @@ export const STATUS = { GACHA_COST_NOT_ENOUGH: { code: 31101, simStr: '招募券不足' }, GACHA_NOT_ASSIGN: { code: 31102, simStr: '请选择武将' }, GACHA_HOPE_NOT_GOLD: { code: 31103, simStr: '武将品质错误' }, - GACHA_CAN_NOT_PICK: { code: 31004, simStr: '不可选择该武将' }, - GACHA_TURNTABLE_POINT_NOT_ENOUGH: { code: 31005, simStr: '转盘积分不足' }, - GACHA_HAS_VISITED: { code: 31006, simStr: '该武将已拜访过' }, - GACHA_VISITED_COUNT_OVER: { code: 31007, simStr: '今天武将拜访已超过次数' }, + GACHA_CAN_NOT_PICK: { code: 31104, simStr: '不可选择该武将' }, + GACHA_TURNTABLE_POINT_NOT_ENOUGH: { code: 31105, simStr: '转盘积分不足' }, + GACHA_HAS_VISITED: { code: 31106, simStr: '该武将已拜访过' }, + GACHA_VISITED_COUNT_OVER: { code: 31107, simStr: '今天武将拜访已超过次数' }, + + // 礼包码 31201-31300 + GIFT_CODE_USED_NUM_MAX: { code: 31201, simStr: '礼包码使用次数超过' }, + YOU_HAVE_USED_THIS_CODE: { code: 31202, simStr: '您已使用过该码' }, + GIFT_CODE_NOT_START: { code: 31203, simStr: '礼包码未生效' }, + GIFT_CODE_HAS_EXPIRED: { code: 31204, simStr: '礼包码已失效' }, + // 社交相关状态 40000 - 49999 SYS_CHANNEL_AUTH_NOT_ENOUGH: { code: 40000, simStr: '无法在系统频道发送消息' }, UPDATE_PRIVATE_MSG_READ_TIME_ERR: { code: 40001, simStr: '更新私聊阅读时间失败' }, diff --git a/shared/db/GiftCode.ts b/shared/db/GiftCode.ts new file mode 100644 index 000000000..cf40d6040 --- /dev/null +++ b/shared/db/GiftCode.ts @@ -0,0 +1,119 @@ +import BaseModel from './BaseModel'; +import { getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose'; +import { CounterModel } from './Counter'; +import { COUNTER } from '../consts'; + +class Rewards { + @prop({ required: true }) + id: number; + @prop({ required: true }) + count: number; +} + +/** + * 举报记录 +**/ +@modelOptions({ schemaOptions: { id: false } }) +export default class GiftCode extends BaseModel { + @prop({ required: true, default: '' }) + id: number; // 唯一id + + @prop({ required: true, default: '' }) + name: string; // 礼包码名 + + @prop({ required: true, default: false }) + isLimit: boolean; // 每个码是否有使用次数限制 + + @prop({ required: true, default: 0 }) + count: number; // 每个码可使用次数 + + @prop({ required: true, type: Rewards, _id: false }) + goods: Rewards[]; // 奖励 + + @prop({ required: true }) + beginTime: Date; // 开始时间 + + @prop({ required: true }) + endTime: Date; // 结束时间 + + @prop({ required: true, default: '' }) + remark: string; // 备注 + + @prop({ required: true, default: 0 }) + generateType: number; // 生成类型 + + @prop({ required: true, default: 0 }) + generateCnt: number; // 已生成条数,giftCodeDetail的数量 + + @prop({ required: true, default: '' }) + generateCode: string; // 单条的生成的那一个code + + @prop({ required: true, default: 0 }) + usedNum: number; // 使用次数 + + public static async findData(id: number) { + let rec: GiftCodeType = await GiftCodeModel.findOne({ id }).lean(); + return rec; + } + + public static async updateData(id: string|number, values: GiftCodeParam, uid = 1) { + if(id == 'new') { + id = await CounterModel.getNewCounter(COUNTER.GIFT_CODE); + } + let doc = new GiftCodeModel(); + let createObj = doc.toJSON(); + delete values.id; + delete createObj._id; + delete createObj.id; + for(let key in values) { + delete createObj[key]; + } + + let rec: GiftCodeType = await GiftCodeModel.findOneAndUpdate({ id }, { $set: {...values, updatedBy: uid}, $setOnInsert: { ...createObj, createdBy: uid } }, + { new: true, upsert: true }).lean(true); + return rec; + } + + public static async increaseUsedNum(id: number) { + let result: GiftCodeType = await GiftCodeModel.findOneAndUpdate({ id }, { $inc: { usedNum: 1 } }, { new: true }).lean(); + return result; + } + + private static getSearchObj(form: { name?: string, current?: boolean }) { + let searchObj = {}; + if (form.name != undefined) searchObj['name'] = { $regex: new RegExp(form.name.toString(), 'i') }; + if (form.current) { + searchObj['beginTime'] = { $lte: new Date }; + searchObj['endTime'] = { $gte: new Date }; + } + return searchObj + } + + public static async findByCondition(page: number, pageSize: number, sortField: string, sortOrder: string, form: { name?: string, current?: boolean } = {}) { + + let searchObj = this.getSearchObj(form); + let sort = {}; + if (sortField && sortOrder) { + if (sortOrder == 'ascend') { + sort[sortField] = 1; + } else if (sortOrder == 'descend') { + sort[sortField] = -1; + } + } + const result: GiftCodeType[] = await GiftCodeModel.find(searchObj, { _id: 0 }).limit(pageSize).skip((page - 1) * pageSize).sort(sort).lean({ getters: true, virtuals: true }); + return result; + + } + + public static async countByCondition(form: { name?: string, current?: boolean } = {}) { + + let searchObj = this.getSearchObj(form); + const result = await GiftCodeModel.count(searchObj); + return result; + } +} + +export const GiftCodeModel = getModelForClass(GiftCode); + +export interface GiftCodeType extends Pick, keyof GiftCode> { } +export type GiftCodeParam = Partial; diff --git a/shared/db/GiftCodeDetail.ts b/shared/db/GiftCodeDetail.ts new file mode 100644 index 000000000..ecc27dce9 --- /dev/null +++ b/shared/db/GiftCodeDetail.ts @@ -0,0 +1,57 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, modelOptions, Ref, mongoose } from '@typegoose/typegoose'; +import GiftCode, { GiftCodeType } from './GiftCode'; +import { genCode } from '../pubUtils/util'; + +/** + * 举报记录 +**/ +@modelOptions({ schemaOptions: { id: false } }) +@index({ code: 1 }) +export default class GiftCodeDetail extends BaseModel { + @prop({ required: true, default: '' }) + code: string; // 兑换码 + + @prop({ ref: 'GiftCode', type: mongoose.Schema.Types.ObjectId }) + giftCode: Ref; + + @prop({ required: true, default: '' }) + usedNum: number; // 该码使用次数 + + + // 根据code + public static async findByCode(code: string) { + let result: GiftCodeDetailType = await GiftCodeDetailModel.findOne({ code }).populate('giftCode').lean(true); + return result; + } + + public static async findByGiftCode(giftCode: GiftCodeType) { + let result: GiftCodeDetailType[] = await GiftCodeDetailModel.find({ giftCode: giftCode._id }).lean(); + return result; + } + + public static async increaseUsedNum(code: string) { + let result: GiftCodeDetailType = await GiftCodeDetailModel.findOneAndUpdate({ code }, { $inc: { usedNum: 1 } }, { new: true }).lean(); + return result; + } + + public static async generateOne(giftCode: GiftCodeType, code: string, uid = 1 ) { + let result = await GiftCodeDetailModel.insertMany([{ giftCode: giftCode._id, code, usedNum: 0, createdBy: uid, updatedBy: uid }]); + return result; + } + + public static async generateMany(giftCode: GiftCodeType, generateNum: number, codeLen: number, uid = 1) { + let insertArr: GiftCodeDetailParam[] = []; + for(let i = 0; i < generateNum; i++) { + insertArr.push({ giftCode: giftCode._id, code: genCode(codeLen), usedNum: 0, createdBy: uid, updatedBy: uid }); + } + let result = await GiftCodeDetailModel.insertMany(insertArr); + return result; + } + +} + +export const GiftCodeDetailModel = getModelForClass(GiftCodeDetail); + +export interface GiftCodeDetailType extends Pick, keyof GiftCodeDetail> { } +export type GiftCodeDetailParam = Partial; diff --git a/shared/db/UserGiftCode.ts b/shared/db/UserGiftCode.ts new file mode 100644 index 000000000..3d14971e7 --- /dev/null +++ b/shared/db/UserGiftCode.ts @@ -0,0 +1,36 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, modelOptions, Ref, mongoose } from '@typegoose/typegoose'; +import GiftCode, { GiftCodeType } from './GiftCode'; + +/** + * 举报记录 +**/ +@modelOptions({ schemaOptions: { id: false } }) +@index({ roleId: 1, code: 1 }) +export default class UserGiftCode extends BaseModel { + + @prop({ required: true, default: '' }) + roleId: string; // 玩家id + + @prop({ required: true, default: '' }) + code: string; // 兑换码 + + @prop({ ref: 'GiftCode', type: mongoose.Schema.Types.ObjectId }) + giftCode: Ref; + + // 根据code + public static async findByCode(roleId: string, code: string) { + let result: UserGiftCodeType = await UserGiftCodeModel.findOne({ roleId, code }).lean(); + return result; + } + + public static async createCode(roleId: string, code: string, gitCode: GiftCodeType) { + let result: UserGiftCodeType = await UserGiftCodeModel.findOneAndUpdate({ roleId, code }, { $setOnInsert: { gitCode: gitCode._id } }, { new: true, upsert: true}).lean() + return result; + } +} + +export const UserGiftCodeModel = getModelForClass(UserGiftCode); + +export interface UserGiftCodeType extends Pick, keyof UserGiftCode> { } +export type UserGiftCodeParam = Partial;