Commit dbd8c94b authored by qinhaitao's avatar qinhaitao

test: 💍 数据接口

parent 04310f41
......@@ -107,6 +107,12 @@ export enum STAT_TYPE {
INITE_SUCCESS
}
export enum CARD_PRIZE_STATUS {
LOCK,
UN_LOCK,
SUCCESS
}
export const appId = '${需要补充}'
// 商铺id 拼接vip链接用
......
/** @format */
import { PRIZE_DATA_TYPE } from '../constants'
import { services, checkParams, preCheck, preUpdate } from '../decorator/common'
import { resultsModel } from '../sdk'
import { CommonCardService } from '../service/common'
import {
checkActivityTime,
checkJoinId,
checkUserInfo,
checkVip,
checkGameTimes,
checkOpenPrizeStatus
} from '../utils/common/check'
import { updateUserInfo, reduceGameTimes } from '../utils/common/update'
import { CommonCardService, CommonAwardsService } from '../service/common'
import { checkActivityTime, checkUserInfo, checkVip, checkInviteId, checkInviteUserCard } from '../utils/common/check'
import { updateUserInfo } from '../utils/common/update'
export interface ISumitGameControllerInfos extends IControllerInfos {
joinInfo: IJoinRecord
......@@ -19,23 +13,115 @@ export interface ISumitGameControllerInfos extends IControllerInfos {
export default class Card {
@checkParams(['activityId'])
@services([CommonCardService])
@services([CommonCardService, CommonAwardsService])
async getCollectCardInfo(
context: IContext<IParams>,
{ userInfo, activityInfo }: IControllerInfos,
[cardService]: [CommonCardService]
[cardService, awardsService]: [CommonCardService, CommonAwardsService]
) {
const { activityId } = context.data
const { joinedTimes, gameTimes } = userInfo
const myCardInfo = cardService.getMyCardInfo(userInfo)
const joinedTimesPrizeList = await cardService.getCardPrizeList(
activityId,
PRIZE_DATA_TYPE.JOIN_TIMES,
'needTimes',
joinedTimes
)
const collectedCardTypePrizeList = await cardService.getCardPrizeList(
activityId,
PRIZE_DATA_TYPE.CARD_TYPE_AWARD,
'needCards',
myCardInfo.cardTypeCollectedCount
)
return resultsModel.success({
joinedTimes,
gameTimes,
...myCardInfo,
joinedTimesPrizeList,
collectedCardTypePrizeList
})
}
@checkParams(['activityId'])
@services([CommonCardService])
@preCheck([checkActivityTime, checkVip])
async doJoin(
@preCheck([
checkActivityTime,
checkVip,
checkUserInfo(
{
gameTimes: { $gte: 1 }
},
'抽卡次数不足'
)
])
@preUpdate([
updateUserInfo({
$where: ` this.gameTimes >= 1`,
$inc: { gameTimes: -1 }
})
])
async doJoin(context: IContext<IParams>, { userInfo }: IControllerInfos, [cardService]: [CommonCardService]) {
const { activityId } = context.data
const cardResult = await cardService.collectCard(activityId, userInfo)
const recordResult = await cardService.addCollectRecord(userInfo, cardResult)
return resultsModel.success({
...cardResult,
...recordResult
})
}
// 获取中奖轮播
@checkParams(['activityId'])
@services([CommonCardService, CommonAwardsService])
async getAwardsCarouselList(
context: IContext<IParams>,
{ userInfo, activityInfo }: IControllerInfos,
[cardService]: [CommonCardService]
[cardService, awardsService]: [CommonCardService, CommonAwardsService]
) {
const { joinedTimes, gameTimes } = userInfo
const { activityId } = context.data
const list = await cardService.getAwardsCarouselList(activityId)
return resultsModel.success({
list
})
}
// 我的卡牌详情
@checkParams(['activityId'])
@services([CommonCardService, CommonAwardsService])
async getMyCardsInfo(context: IContext<IParams>, { userInfo }: IControllerInfos, [cardService]: [CommonCardService]) {
const { activityId } = context.data
const list = await cardService.getMyCardsInfo(activityId, userInfo)
return resultsModel.success({
list
})
}
// 获取好友赠送卡片
@checkParams(['activityId', 'type', 'inviteId'])
@services([CommonCardService, CommonAwardsService])
@preCheck([checkActivityTime, checkInviteId, checkInviteUserCard])
async getGiftCard(
context: IContext<IParams>,
{ userInfo }: IControllerInfos,
[cardService]: [CommonCardService],
{ inviteUserInfo }: IPreCheckData
) {
const { activityId, type } = context.data
const giftResult = await cardService.getGiftCard(activityId, type, userInfo, inviteUserInfo)
return resultsModel.success(giftResult)
}
}
......@@ -16,7 +16,7 @@ export default function preCheck(checks: IFunction[]) {
for (let i = 0; i < checks.length; i++) {
const checkFn = checks[i]
let result = await checkFn.apply(target, [context, { ...otherArgs }, services])
let result = await checkFn.apply(target, [context, { ...otherArgs }, services, preCheckData])
result = result || {}
// 校验报错
if ((result as IErrorResult)?.success === false && (result as IErrorResult)?.code) {
......
......@@ -17,6 +17,9 @@ import CustomTest1Controller from './controller/custom/test1.controller'
const CustomTest1ControllerInstance = new CustomTest1Controller()
import CustomTest2Controller from './controller/custom/test2.controller'
const CustomTest2ControllerInstance = new CustomTest2Controller()
import CommonCardController from './controller/card.controller'
const { getAwardsCarouselList, getCollectCardInfo, getGiftCard, getMyCardsInfo, doJoin } = new CommonCardController()
export default {
getVipInfo: CommonUserControllerInstance.getVipInfo,
......@@ -38,5 +41,11 @@ export default {
testAddStat: CustomTest1ControllerInstance.testAddStat,
testGetStats: CustomTest1ControllerInstance.testGetStats,
test2addStat: CustomTest2ControllerInstance.test2addStat,
test2getStats: CustomTest2ControllerInstance.test2getStats
test2getStats: CustomTest2ControllerInstance.test2getStats,
// 卡牌类
getAwardsCarouselList,
getCollectCardInfo,
getGiftCard,
getMyCardsInfo,
doJoin
}
......@@ -285,11 +285,17 @@ export default class AwardsService extends UserService {
}
// 根据查询条件获取奖品配置
async getPrizeConfig(query: { activityId: string; [queryParam: string]: any }): Promise<IActivityPrize[]> {
return this.activityprizedao.find({
deleteStatus: DELETE_STATUS.NORMAL,
...query
})
async getPrizeConfig(
query: { activityId: string; [queryParam: string]: any },
projection: IFindProjection = {}
): Promise<IActivityPrize[]> {
return this.activityprizedao.find<IActivityPrize>(
{
deleteStatus: DELETE_STATUS.NORMAL,
...query
},
projection
)
}
// 根据概率抽取奖品
......
......@@ -4,20 +4,243 @@
* @format
*/
import { BaseDao, TBAPIS } from '../../sdk'
import { BaseDao, resultsModel, TBAPIS } from '../../sdk'
import { JOIN_DB_NAME } from '../../db'
import { ACTIVITY_STATUS } from '../../constants'
import { ACTIVITY_STATUS, CARD_PRIZE_STATUS, PRIZE_DATA_TYPE, PRIZE_TYPE } from '../../constants'
import { getToday } from '../../utils'
import UserService from './user.service'
import AwardService from './awards.service'
import { CODE_TYPES } from '../../errorCode'
import { uniq } from 'lodash'
export default class CardService extends UserService {
context: IContext<IParams>
joindao: IBaseDao
awardService: AwardService
constructor(context: IContext<IParams>) {
super(context)
this.joindao = new BaseDao(context, JOIN_DB_NAME)
this.awardService = new AwardService(context)
}
// 集卡
async collectCard(activityId: string) {}
async collectCard(activityId: string, userInfo: IUserInfo) {
const card = await this.getCard(activityId)
const { cardType, _id, image, name, type } = card
if (type === PRIZE_TYPE.THANKS)
return {
type: PRIZE_TYPE.THANKS,
name: '谢谢参与'
}
const reduceResult = await this.awardService.reduceStock(_id)
if ((reduceResult as ICodeType)?.code) return CODE_TYPES.ERROR_NO_STOCK
userInfo.cardInfo[cardType] = (userInfo.cardInfo?.[cardType] || 0) + 1
const updateResult = await this.updateUser(userInfo._id, {
$inc: {
joinedTimes: 1,
[`cardInfo.${cardType}`]: 1
}
})
if (updateResult !== 1) return CODE_TYPES.SYSTEM_ERROR
const myCardInfo = this.getMyCardInfo(userInfo, cardType)
return {
cardType,
image,
name,
type,
...myCardInfo
}
}
getMyCardInfo(userInfo: IUserInfo, cardType?: number) {
const { cardInfo } = userInfo
// 该卡片收集数量
const currCardCollectedCount = cardType ? cardInfo?.[cardType] || 0 : undefined
// 已收集卡片的总数量
const cardQuantityCollectedCount = Object.values(cardInfo).reduce((total, count) => total + count, 0)
// 已收集卡片类型
const cardTypeCollectedCount = Object.keys(cardInfo).length
return {
currCardCollectedCount,
cardQuantityCollectedCount,
cardTypeCollectedCount
}
}
async addCollectRecord(userInfo: IUserInfo, cardResult: object) {
const { openId } = this.context
const { activityId } = this.context.data
const { userNick } = userInfo
const recordId = await this.joindao.insertOne({
activityId,
userNick,
openId,
...cardResult,
createTime: Date.now(),
createDay: getToday()
})
return { recordId }
}
// 获取卡片
async getCard(activityId: string) {
const cardPool = await this.awardService.getPrizeConfig({
activityId,
prizeDataType: PRIZE_DATA_TYPE.CARD
})
let card = await this.awardService.getPrizeByProbability(cardPool)
const thanksPrize = cardPool.find(v => v.type === PRIZE_TYPE.THANKS) || {
type: PRIZE_TYPE.THANKS,
prizeDataType: PRIZE_DATA_TYPE.CARD,
name: '谢谢参与'
}
!card && (card = thanksPrize)
return card
}
async getCardPrizeList(activityId: string, prizeDataType: number, needKey: string, userCount: number) {
const { openId } = this.context
const joinedTimesPrizePool = await this.awardService.getPrizeConfig(
{
activityId,
prizeDataType
},
{
projection: { type: 1, image: 1, prizeDataType: 1, name: 1 }
}
)
// 抽奖次数奖池分类 从小到大排列
const category = uniq(joinedTimesPrizePool.map(v => +v[needKey])).sort()
const prizeMapper = category.reduce((prev, curr) => {
return {
...prev,
[`${curr}`]: joinedTimesPrizePool.filter(v => v[needKey] === curr)
}
}, {})
const awardList = await this.awardService.getAwardsInfoList(
{
activityId,
openId,
prizeDataType
},
{
projection: { type: 1, image: 1, prizeDataType: 1, name: 1 }
}
)
const prizeList = category.map(key => {
return {
[needKey]: key,
status: setCardPrizeStatus(userCount, key, awardList),
prizeList: prizeMapper[key]
}
})
function setCardPrizeStatus(userCount: number, needCount: number, awardList: IAwards[]) {
if (userCount < needCount) return CARD_PRIZE_STATUS.LOCK
if (needCount >= userCount && !awardList.some(v => v[needKey] === needCount)) return CARD_PRIZE_STATUS.UN_LOCK
if (needCount >= userCount && awardList.some(v => v[needKey] === needCount)) return CARD_PRIZE_STATUS.SUCCESS
}
return prizeList
}
async getAwardsCarouselList(activityId: string, limit: number = 30) {
const list = await this.awardService.getAwardsInfoList(
{
activityId,
prizeDataType: {
$in: [PRIZE_DATA_TYPE.CARD_TYPE_AWARD, PRIZE_DATA_TYPE.JOIN_TIMES]
}
},
{
sort: {
createTime: 1
},
projection: {
name: 1,
image: 1,
type: 1,
prizeDataType: 1,
avatar: 1,
userNick: 1
},
limit
}
)
return list
}
async getMyCardsInfo(activityId: string, userInfo: IUserInfo) {
const { cardInfo } = userInfo
const cardPool = await this.awardService.getPrizeConfig({
activityId,
prizeDataType: PRIZE_DATA_TYPE.CARD
})
return Object.keys(cardInfo).map(type => {
const { name, image } = cardPool.find(v => v.cardType === +type) || {}
return {
type,
name,
image,
count: cardInfo[type] || 0
}
})
}
async getGiftCard(activityId: string, type: number, userInfo: IUserInfo, inviteUserInfo: IUserInfo) {
// 扣除邀请者的对应卡牌
const reduceResult = await this.userdao.update(
{
_id: inviteUserInfo._id,
$where: `this.cardInfo.${type} >= 1`
},
{
$inc: { [`cardInfo.${type}`]: -1 }
}
)
if (!reduceResult) return CODE_TYPES.SYSTEM_ERROR
// 增加受赠者的卡牌
const addResult = await this.updateUser(userInfo._id, {
$inc: { [`cardInfo.${type}`]: -1 }
})
if (!addResult) return CODE_TYPES.SYSTEM_ERROR
const cardInfo =
(await this.awardService.getPrizeConfig({
activityId,
prizeDataType: PRIZE_DATA_TYPE.CARD,
cardType: +type
})?.[0]) || {}
const { name, image } = cardInfo
return {
name,
image,
type
}
}
}
/** @format */
interface IAwards {
type IAwards = ICommonAwards & ICustomAwards
interface ICustomAwards {
needTimes?: number
}
interface ICommonAwards {
_id?: string
id?: string
openId: string
......
/** @format */
type IActivityPrize = ICommonActivityPrize & ICustomActivityPrize
interface IActivityPrize {
interface ICustomActivityPrize {
cardType?: number
needTimes?: number
needCards?: number
}
interface ICommonActivityPrize {
_id?: string
id?: string
activityId?: string
......
......@@ -17,3 +17,5 @@ interface ICodeType {
}
type IResult<T> = T extends ICodeType ? IErrorResult : IErrorResult | ISuccessResult<T>
type IPreCheckResult = IErrorResult | { [key: string]: any }
......@@ -3,7 +3,18 @@
import { resultsModel } from '../../../sdk'
import { CODE_TYPES } from '../../../errorCode'
export default async function checkActivityTime(context: IContext<IParams>, { activityInfo }: IControllerInfos) {
/**
* 检查活动时间
*
* @export
* @param {IContext<IParams>} context
* @param {IControllerInfos} { activityInfo }
* @return {*} {(Promise<IPreCheckResult}
*/
export default async function checkActivityTime(
context: IContext<IParams>,
{ activityInfo }: IControllerInfos
): Promise<IPreCheckResult> {
// 活动不存在
if (!activityInfo) {
return resultsModel.error(CODE_TYPES.ERROR_NO_ACTIVITY)
......
......@@ -4,7 +4,7 @@ import { resultsModel } from '../../../sdk'
import { CODE_TYPES } from '../../../errorCode'
import { isBoolean } from 'lodash'
export default async function checkFollow(context: IContext<IParams>) {
export default async function checkFollow(context: IContext<IParams>): Promise<IPreCheckResult> {
const { isFollow } = context.data
if (!isBoolean(isFollow)) return resultsModel.error(CODE_TYPES.PARAMS_ERROR, '缺少isFollow参数')
......
/** @format */
import { resultsModel } from '../../../sdk'
import { CODE_TYPES } from '../../../errorCode'
import { isBoolean } from 'lodash'
export default async function checkInviteUserCard(
context: IContext<{ type: number; activityId: string; inviteId: string }>,
{}: IControllerInfos,
[],
{ inviteUserInfo }: IPreCheckData
): Promise<IPreCheckResult> {
const { type } = context.data
if (!inviteUserInfo) {
console.error(`checkInviteUserCard必须搭配 checkInviteId使用`)
}
const { cardInfo } = inviteUserInfo
if (!cardInfo?.type) return resultsModel.error(CODE_TYPES.SYSTEM_ERROR, '邀请者该卡片不足')
}
......@@ -12,7 +12,7 @@ import checkVip from './checkVip'
import checkUserInfo, { checkGameTimes } from './checkUserInfo'
import checkJoinId from './checkJoinId'
import checkOpenPrizeStatus from './checkOpenPrizeStatus'
import checkInviteUserCard from './checkInviteUserCard'
const check = {
checkActivityTime,
checkExchangeCreditsTask,
......@@ -29,7 +29,8 @@ const check = {
checkUserInfo,
checkJoinId,
checkGameTimes,
checkOpenPrizeStatus
checkOpenPrizeStatus,
checkInviteUserCard
}
export default check
......@@ -50,5 +51,6 @@ export {
checkUserInfo,
checkJoinId,
checkGameTimes,
checkOpenPrizeStatus
checkOpenPrizeStatus,
checkInviteUserCard
}
......@@ -28,7 +28,7 @@ export default async function updateOrderGoods(
const orderResult = await getUserOrderlist(
this.context,
//@ts-ignore
activityInfo?.startTime || Date.now(),
userInfo.createTime || activityInfo?.startTime || Date.now(),
Date.now()
)
......
/** @format */
import { CARD_PRIZE_STATUS } from '../../constants'
export function setCardPrizeStatus(joinedTimes: number, needTimes: number, joinedTimesAwardList: IAwards[]) {
if (joinedTimes < needTimes) return CARD_PRIZE_STATUS.LOCK
if (needTimes >= joinedTimes && !joinedTimesAwardList.some(v => v.needTimes === needTimes))
return CARD_PRIZE_STATUS.UN_LOCK
if (needTimes >= joinedTimes && joinedTimesAwardList.some(v => v.needTimes === needTimes))
return CARD_PRIZE_STATUS.SUCCESS
}
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