Commit 440b6f73 authored by qinhaitao's avatar qinhaitao

feat: 🎸 经验值

parent 6b32a31d
......@@ -5,13 +5,15 @@ import { cfg as test } from './test'
export interface IConfig {
vipCallBackUrl?: string
INTERACT_TASKS: IGrowInteractTasks
INTERACT_TASKS: IGrowInteractTaskConfig
SEEDS: ISeed[]
CONTINUED_GAINS: IGains
CONTINUED_GAINS_CONFIG: IGains
EXP_CONFIG: IExpConfig[]
}
const configs = {
online,
pre: online,
test,
mock: test
}
......
......@@ -21,14 +21,25 @@ export const cfg: IConfig = {
config: [
{
level: 1,
gapTime: 2 * 60 * 60 * 1000
cdTime: 2 * 60 * 60 * 1000
}
]
}
],
CONTINUED_GAINS: {
CONTINUED_GAINS_CONFIG: {
key: 'milkDrops',
maxLimit: 8,
gapTime: 2 * 60 * 60 * 1000
value: 1,
cdTime: 2 * 60 * 60 * 1000
},
EXP_CONFIG: [
{
level: 1,
exp: 1000
},
{
level: 2,
exp: 8000
}
]
}
......@@ -24,14 +24,25 @@ export const cfg: IConfig = {
config: [
{
level: 1,
gapTime: 2 * 60 * 60 * 1000
cdTime: 2 * 60 * 60 * 1000
}
]
}
],
CONTINUED_GAINS: {
CONTINUED_GAINS_CONFIG: {
key: 'milkDrops',
maxLimit: 8,
gapTime: 2 * 60 * 60 * 1000
value: 1,
cdTime: 2 * 60 * 60 * 1000
},
EXP_CONFIG: [
{
level: 1,
exp: 1000
},
{
level: 2,
exp: 8000
}
]
}
......@@ -55,4 +55,19 @@ export default class Grow {
})
return resultsModel.success(result)
}
/**
* 获取养成信息
*/
@checkParams(['activityId'])
@services([GrowService])
async getGrowGameInfo(
context: IContext<IParams>,
{ userInfo, activityInfo }: IControllerInfos,
[growService]: [GrowService]
) {
const growGameInfo = await growService.getGrowInfo(context, activityInfo, userInfo)
return resultsModel.success(growGameInfo)
}
}
......@@ -13,6 +13,7 @@ import {
} from '../utils/common/check'
import { preUpdate } from '../decorator/common'
import { updateVip, updateSignGrowTask, updateGrowOrderGoods } from '../utils/common/update'
import { checkInteractTaskValid } from '../utils/common/check/growTask'
export default class Task {
/**
......@@ -71,14 +72,35 @@ export default class Task {
{ userInfo }: IControllerInfos,
[growTaskService]: [CommonGrowtaskService]
): Promise<IResult<{ rewards: number }>> {
const { rewardsKey } = context.data
const { rewardsKey = 'gameTimes' } = context.data
const { taskType } = context.data
const rewardsResult = await growTaskService.receiveTaskRewards(taskType, rewardsKey || 'gameTimes', userInfo)
const rewardsResult = await growTaskService.receiveTaskRewards(taskType, rewardsKey, userInfo)
return resultsModel.success(rewardsResult)
}
/**
*
* 做互动任务
*
*/
@checkParams(['activityId', 'type'])
@services([CommonGrowtaskService])
@preCheck([checkActivityTime, checkInteractTaskValid])
async doInteractTask(
context: IContext<IParams>,
{ userInfo }: IControllerInfos,
[growTaskService]: [CommonGrowtaskService]
) {
const { type, activityId } = context.data
const rewardsKey = 'milk'
const result = await growTaskService.doInteractTask(activityId, type, rewardsKey, userInfo)
return resultsModel.success(result)
}
/**
* 获取收藏商品列表
*/
......
......@@ -12,6 +12,8 @@ export const SIGN_DB_NAME = 'c_user_sign'
export const TASK_DB_NAME = 'c_user_tasks'
export const INTERACT_TASK_DB_NAME = 'c_interact_tasks'
export const STAT_DB_NAME = 'c_stats'
export const ERROR_LOG_DB_NAME = 'error_log'
......
/** @format */
import { getConfig } from '../../config'
import { formatExp } from '../../utils/common/grow'
export function injectGrowExp(target: any, name: string, descriptor: PropertyDescriptor) {
const method = descriptor.value
descriptor.value = async function (
context: IContext<IParams>,
activityInfo: IActivityInfo,
userInfo: IUserInfo,
gameInfoData = {}
) {
const { exp } = userInfo
const { EXP_CONFIG } = getConfig(context)
const expLevel = formatExp(exp, EXP_CONFIG)
return method.apply(this, [context, activityInfo, userInfo, { ...gameInfoData, expLevel }])
}
}
export function injectContinuedGains(target: any, name: string, descriptor: PropertyDescriptor) {
const method = descriptor.value
descriptor.value = async function (
context: IContext<IParams>,
activityInfo: IActivityInfo,
userInfo: IUserInfo,
gameInfoData = {}
) {
let { continuedGains } = userInfo
const {
CONTINUED_GAINS_CONFIG: { maxLimit }
} = getConfig(context)
if (continuedGains.length < maxLimit) {
}
return method.apply(this, [context, activityInfo, userInfo, { ...gameInfoData }])
}
}
......@@ -122,6 +122,30 @@ export const BusinessError = {
ERROR_PRIZE_EXPIRED: {
code: `740001`,
defaultMsg: `奖品已超过领取时间`
},
ERROR_HAVE_GIVE_NAME: {
code: `840001`,
defaultMsg: `请勿重复命名`
},
ERROR_MAX_LENGTH_NAME: {
code: `840002`,
defaultMsg: `您的命名超过最大长度`
},
ERROR_INVALID_INTERACT_TASK_TYPE: {
code: `840003`,
defaultMsg: `互动任务类型错误`
},
ERROR_INVALID_CD_TIME: {
code: `840004`,
defaultMsg: `该任务正在冷却中,请稍后再试`
},
ERROR_INTERACT_TASK_DAY_LIMIT: {
code: `840005`,
defaultMsg: `该任务超过每日限额,明天再来吧`
},
ERROR_INTERACT_TASK_TOTAL_LIMIT: {
code: `840006`,
defaultMsg: `该任务超过总限额,去做别的任务吧`
}
}
......
/** @format */
import CommonUserController from './controller/common/user.controller'
const CommonUserControllerInstance = new CommonUserController()
import CommonTaskController from './controller/common/task.controller'
......@@ -16,8 +17,15 @@ const CommonShareControllerInstance = new CommonShareController()
import CustomTest1Controller from './controller/custom/test1.controller'
const CustomTest1ControllerInstance = new CustomTest1Controller()
import CustomTest2Controller from './controller/custom/test2.controller'
import { CommonGrowtaskService } from './service/common'
const CustomTest2ControllerInstance = new CustomTest2Controller()
import GrowTaskController from './controller/growTask.controller'
const { doInteractTask } = new GrowTaskController()
import GrowController from './controller/grow.controller'
const { getGrowGameInfo } = new GrowController()
export default {
getVipInfo: CommonUserControllerInstance.getVipInfo,
getRankList: CommonUserControllerInstance.getRankList,
......@@ -36,6 +44,8 @@ export default {
endOfActivityRewards: CommonAwardsControllerInstance.endOfActivityRewards,
getShareInfo: CommonShareControllerInstance.getShareInfo,
doHelp: CommonShareControllerInstance.doHelp,
doInteractTask,
getGrowGameInfo,
testAddStat: CustomTest1ControllerInstance.testAddStat,
testGetStats: CustomTest1ControllerInstance.testGetStats,
test2addStat: CustomTest2ControllerInstance.test2addStat,
......
......@@ -8,6 +8,7 @@ import { BaseDao, TBAPIS } from '../../sdk'
import { ACTIVITY_CONFIG_DB_NAME } from '../../db'
import { ACTIVITY_OPEN_PRIZE_STATUS, ACTIVITY_STATUS } from '../../constants'
import UserService from './user.service'
import { injectGrowExp } from '../../decorator/common/InjectGrowInfo'
export default class GrowService extends UserService {
context: IContext<any>
......@@ -17,4 +18,16 @@ export default class GrowService extends UserService {
this.context = context
this.activitydao = new BaseDao(context, ACTIVITY_CONFIG_DB_NAME)
}
@injectGrowExp
async getGrowInfo(
context: IContext<IParams>,
activityInfo: IActivityInfo,
userInfo: IUserInfo,
injectedGrowInfoData: IInjectedGrowInfoData = {}
) {
return {
...injectedGrowInfoData
}
}
}
......@@ -8,7 +8,7 @@ import UserService from './user.service'
import { BaseDao } from '../../sdk'
import { getToday, getUserOrderlist, generateVipUrl, formatVipCbUrl, setNewFollowUserData } from '../../utils'
import { TASK_RATE_TYPE, TASK_STATUS } from '../../constants'
import { TASK_DB_NAME } from '../../db'
import { INTERACT_TASK_DB_NAME, TASK_DB_NAME } from '../../db'
import {
getTotalCompleteTask,
setGrowTaskStatus,
......@@ -17,6 +17,7 @@ import {
getOrderCount
} from '../../utils/common/task'
import { CODE_TYPES } from '../../errorCode'
import { getConfig } from '../../config'
export interface ITaskInfo {
taskType?: string // 任务类型
......@@ -33,9 +34,11 @@ export interface ITaskInfo {
}
export default class TaskService extends UserService {
taskInfodao: IBaseDao
interactTaskDao: IBaseDao
constructor(context: IContext<IParams>) {
super(context)
this.taskInfodao = new BaseDao(context, TASK_DB_NAME)
this.interactTaskDao = new BaseDao(context, INTERACT_TASK_DB_NAME)
}
// 根据活动tasks字段渲染任务
......@@ -245,4 +248,48 @@ export default class TaskService extends UserService {
return result
}
/**
* 完成互动任务
*
* @param {string} activityId
* @param {IGrowInteractTaskType} type
* @param {string} rewardsKey
* @param {IUserInfo} userInfo
* @return {string}
* @memberof TaskService
*/
async doInteractTask(
activityId: string,
type: IGrowInteractTaskType,
rewardsKey: string,
userInfo: IUserInfo
): Promise<string> {
const { openId } = this.context
const { userNick } = userInfo
const now = Date.now()
const insertId = await this.interactTaskDao.insertOne<IDBInteractTasks>({
activityId,
openId,
userNick,
type,
createDay: getToday(),
createTime: now,
updateTime: now
})
const { rewards } = getConfig(this.context)?.['INTERACT_TASKS']?.[type] || {}
// 更新奖励
if (rewards) {
await this.updateUser(userInfo._id, {
$inc: {
[rewardsKey]: rewards
}
})
}
return insertId
}
}
......@@ -34,28 +34,6 @@ type ITaskType =
type ITasks = { [key in ITaskType]: ITaskConfig }
type IGrowInteractTaskType = 'bath' | 'music'
type IGrowInteractTasks = {
[key in IGrowInteractTaskType]: {
cdTime: number
rewards: number
dayLimit?: number
totalLimit?: number
}
}
interface ISeedConfig {
level: number
gapTime: number
}
type ISeed = {
name: string
type: IPlantType
config: ISeedConfig[]
}
interface ITaskConfig {
title?: string // 任务标题
value: number // 任奖奖励
......
/** @format */
type IGrowInteractTaskType = 'bath' | 'music'
type IGrowInteractTaskConfig = {
[key in IGrowInteractTaskType]: {
cdTime: number
rewards: number
dayLimit?: number
totalLimit?: number
}
}
interface ISeedConfig {
level: number
cdTime: number
}
type ISeed = {
name: string
type: IPlantType
config: ISeedConfig[]
}
interface IExpConfig {
level: number
exp: number
}
interface IDBInteractTasks {
openId: string
userNick: string
activityId: string
type: IGrowInteractTaskType
createTime: number
updateTime: number
createDay: string
}
interface IInjectedGrowInfoData {
expLevel?: any
}
interface IContinuedGain {
pos: number // 位置
value: number // 数据
createTime: number // 创建时间
}
......@@ -14,7 +14,8 @@ type TGainsKey = 'milkDrops'
interface IGains {
key: TGainsKey
maxLimit: number
gapTime: number
cdTime: number
value: number
}
interface ICustomUserInfo {
// 游戏类
......@@ -26,6 +27,8 @@ interface ICustomUserInfo {
guideStep: number
name: string
plants?: IPlantInfo[]
continuedGains?: IContinuedGain[]
exp?: number
storages?: {
[key in IPlantType]?: number
}
......
......@@ -18,13 +18,13 @@ export async function checkGuideStep(context: IContext<IParams>, { userInfo }: I
* @param {number} [maxLength=7]
* @return {*}
*/
export function checkName(maxLength = 7) {
export function checkName(maxLength = 7): IFunction {
return async function (context: IContext<IParams>, { userInfo }: IControllerInfos) {
const { name } = userInfo
if (name) return resultsModel.error(CODE_TYPES.PARAMS_ERROR, '您已经命名过~')
if (name) return resultsModel.error(CODE_TYPES.ERROR_HAVE_GIVE_NAME, '您已经命名过~')
if (name?.length > maxLength)
return resultsModel.error(CODE_TYPES.PARAMS_ERROR, `您的命名超过最大长度,最多${maxLength}个字符`)
return resultsModel.error(CODE_TYPES.ERROR_MAX_LENGTH_NAME, `您的命名超过最大长度,最多${maxLength}个字符`)
}
}
/** @format */
import { resultsModel } from '../../../sdk'
import { CODE_TYPES } from '../../../errorCode'
import { formatVipCbUrl, getShopVip } from '../vip'
import { TOTAL_GUIDE_STEP } from '../../../constants'
import { getConfig } from '../../../config'
import { dbCount, dbFindOne } from '../mongodb'
import { INTERACT_TASK_DB_NAME } from '../../../db'
import { getToday } from '../getToday'
// 检查是否可以做活动任务
export async function checkInteractTaskValid(
context: IContext<{
activityId: string
type: IGrowInteractTaskType
}>
) {
const { type, activityId } = context.data
const { openId } = context
const { INTERACT_TASKS } = getConfig(context)
const interactTaskConfig = INTERACT_TASKS?.[type]
if (!interactTaskConfig) return resultsModel.error(CODE_TYPES.ERROR_INVALID_INTERACT_TASK_TYPE)
const { cdTime, dayLimit, totalLimit } = interactTaskConfig
// 获取最新的任务记录
const latestTaskInfo = await findLatestInteractTaskInfo(context, { activityId, type, openId })
const now = Date.now()
if (!latestTaskInfo) return {}
const { createTime } = latestTaskInfo
const gapTime = now - createTime
// 任务冷却中
const isCD = gapTime > 0 && gapTime <= cdTime
if (isCD) return resultsModel.error(CODE_TYPES.ERROR_INVALID_CD_TIME)
// 每日互动任务限制
if (dayLimit) {
const todayCount = await countTodayInteractTask(context, { activityId, type, openId })
if (todayCount >= dayLimit) return resultsModel.error(CODE_TYPES.ERROR_INTERACT_TASK_DAY_LIMIT)
}
// 总次数限制
if (totalLimit) {
const totalCount = await countTotalInteractTask(context, { activityId, type, openId })
if (totalCount >= totalLimit) return resultsModel.error(CODE_TYPES.ERROR_INTERACT_TASK_TOTAL_LIMIT)
}
}
function findLatestInteractTaskInfo(
context: IContext<IParams>,
{ activityId, type, openId }: { activityId: string; type: IGrowInteractTaskType; openId: string }
) {
return dbFindOne<IDBInteractTasks>(
context,
INTERACT_TASK_DB_NAME,
{ activityId, openId, type },
{ sort: { createTime: -1 } }
)
}
function countTodayInteractTask(
context: IContext<IParams>,
{ activityId, type, openId }: { activityId: string; type: IGrowInteractTaskType; openId: string },
day?: string
) {
return dbCount(context, INTERACT_TASK_DB_NAME, { activityId, openId, type, createDay: day || getToday() })
}
function countTotalInteractTask(
context: IContext<IParams>,
{ activityId, type, openId }: { activityId: string; type: IGrowInteractTaskType; openId: string },
day?: string
) {
return dbCount(context, INTERACT_TASK_DB_NAME, { activityId, openId, type })
}
/** @format */
// 养成相关
/**
* 根据经验配置获取当前等级
*
* @export
* @param {number} exp
* @param {IExpConfig[]} expConfig
* @return {*}
*/
export function formatExp(exp: number, expConfig: IExpConfig[]) {
const currExpInfo =
expConfig.find((config, i) => {
const fromExp = i === 0 ? 0 : expConfig[i - 1]?.exp
const toExp = config.exp
return exp >= fromExp && exp < toExp
}) || ({} as IExpConfig)
return {
exp,
expLevel: currExpInfo?.level || 'max'
}
}
/**
* 计算需要新创建持续作物的数量
*
* @export
* @param {IContinuedGain[]} continuedGains
* @param {IGains} gainConfig
* @return {*} {number}
*/
export function countNewCreateContinuedGains(
userInfo: IUserInfo,
continuedGains: IContinuedGain[],
gainConfig: IGains,
speedUpCdTime?: number
): number {
const { maxLimit, cdTime } = gainConfig
const now = Date.now()
const finalCDTime = speedUpCdTime || cdTime
// 已经超过最大限制
if (continuedGains.length >= maxLimit) return 0
// const gapTime = now -
}
......@@ -2,14 +2,24 @@
import { BaseDao } from '../../sdk'
export async function dbFindOne<T>(context: IContext<IParams>, dbName: string, query: IMongoQuery) {
export async function dbFindOne<T>(
context: IContext<IParams>,
dbName: string,
query: IMongoQuery,
projection?: IFindProjection
) {
const dao: IBaseDao = new BaseDao(context, dbName)
return await dao.findOne<T>(query)
return await dao.findOne<T>(query, projection)
}
export async function dbFind<T>(context: IContext<IParams>, dbName: string, query: IMongoQuery) {
export async function dbFind<T>(
context: IContext<IParams>,
dbName: string,
query: IMongoQuery,
projection?: IFindProjection
) {
const dao: IBaseDao = new BaseDao(context, dbName)
return await dao.find<T>(query)
return await dao.find<T>(query, projection)
}
export async function dbCount(context: IContext<IParams>, dbName: string, query: IMongoQuery) {
......
......@@ -69,7 +69,7 @@ export default async function updateGrowOrderGoods(
!orderList.some(order => order.orderId === v.orderId) &&
!targetOrders.some(order => order.orderId === v.orderId)
) {
if (targetOrders.length + completeTimes >= times) {
if (targetOrders.length + completeTimes >= times && taskRateType !== TASK_RATE_TYPE.NOLIMIT) {
return
}
targetOrders.push({
......
......@@ -787,6 +787,11 @@ punycode@^2.1.0:
resolved "https://registry.npm.taobao.org/punycode/download/punycode-2.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpunycode%2Fdownload%2Fpunycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha1-tYsBCsQMIsVldhbI0sLALHv0eew=
reflect-metadata@^0.1.13:
version "0.1.13"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
regexpp@^3.0.0, regexpp@^3.1.0:
version "3.1.0"
resolved "https://registry.npm.taobao.org/regexpp/download/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment