Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Zamelane committed Dec 8, 2024
1 parent 00ee886 commit 6eb427f
Show file tree
Hide file tree
Showing 37 changed files with 769 additions and 161 deletions.
1 change: 1 addition & 0 deletions apps/api/src/models/BalanceOperation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './model'
56 changes: 56 additions & 0 deletions apps/api/src/models/BalanceOperation/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { DataTypes } from 'sequelize'

import { cache, enableCache, sequelize } from '@db'

import type {IModelPrototype} from '../types'
import {Nullable} from "@diary-spo/shared";
import {DiaryUserModel} from "../DiaryUser";

export type BalanceOperationModelType = {
id: bigint
diaryUserId: bigint
senderId: Nullable<bigint>
comment: Nullable<string>
amount: number
datetime?: string
}

export type IBalanceOperationModelType = IModelPrototype<BalanceOperationModelType, 'id'>

const balanceOperationModel = sequelize.define<IBalanceOperationModelType>('balanceOperation', {
id: {
type: DataTypes.BIGINT,
primaryKey: true,
autoIncrement: true
},
senderId: {
type: DataTypes.BIGINT,
allowNull: true,
references: {
model: DiaryUserModel
}
},
diaryUserId: {
type: DataTypes.BIGINT,
references: {
model: DiaryUserModel
}
},
comment: {
type: DataTypes.STRING,
allowNull: true
},
amount: {
type: DataTypes.INTEGER,
allowNull: false
},
datetime: {
type: DataTypes.DATEONLY,
allowNull: false,
defaultValue: Date.now()
}
})

export const BalanceOperationModel = enableCache
? cache.init<IBalanceOperationModelType>(balanceOperationModel)
: balanceOperationModel
2 changes: 1 addition & 1 deletion apps/api/src/models/DiaryUser/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type DiaryUserModelType = {
termStartDate?: Nullable<string>
isAdmin: boolean
idFromDiary: number
avatarId: bigint
avatarId: Nullable<bigint>
}

export type IDiaryUserModel = IModelPrototype<DiaryUserModelType, 'id'>
Expand Down
6 changes: 4 additions & 2 deletions apps/api/src/models/UserAvatar/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ const userAvatarModel = sequelize.define<IUserAvatarModelType>('userAvatar', {
references: {
model: AvatarModel
},
allowNull: false
allowNull: false,
primaryKey: true
},
diaryUserId: {
type: DataTypes.BIGINT,
references: {
model: DiaryUserModel
},
allowNull: false
allowNull: false,
primaryKey: true
}
})

Expand Down
5 changes: 5 additions & 0 deletions apps/api/src/models/relations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { TermTypeModel } from './TermType'
import { TermUserModel } from './TermUser'
import { ThemeModel } from './Theme'
import { UserAvatarModel } from './UserAvatar'
import {BalanceOperationModel} from "./BalanceOperation";

// SPO <--->> Group
SPOModel.hasMany(GroupModel)
Expand Down Expand Up @@ -217,6 +218,10 @@ AvatarTagModel.belongsTo(AvatarModel)
TagModel.hasMany(AvatarTagModel)
AvatarTagModel.belongsTo(TagModel)

// DiaryUser <-->> BalanceOperation
DiaryUserModel.hasMany(BalanceOperationModel)
BalanceOperationModel.belongsTo(DiaryUserModel)

if (forceSyncDatabase) {
console.log('Syncing database...')
await sequelize.sync()
Expand Down
6 changes: 5 additions & 1 deletion apps/api/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { AuthController } from './auth'
import { FinalMarksController } from './finalMarks'
import { HomeController } from './home'
import { LessonsController } from './lessons'
import { MarketAvatars } from './marketAvatars'
import {BuyAvatar, MarketAvatars, UserAvatars, UserInfo, UserSaveAvatar} from './market'
import { OrganizationController } from './organization'
import { PerformanceCurrentController } from './performance.current'

Expand All @@ -22,6 +22,10 @@ export const routes = new Elysia()
.use(FinalMarksController)
.use(PerformanceCurrentController)
.use(MarketAvatars)
.use(UserInfo)
.use(BuyAvatar)
.use(UserAvatars)
.use(UserSaveAvatar)
/** Обработка любых ошибок в кажом роуте **/
.onError(({ set, code, path, error }) => {
if (Number(code)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { sequelize } from '@db'
import type { AvatarData } from '@diary-spo/shared'
import { AvatarModel, type IAvatarModelType } from '../../models/Avatar'
import { AvatarModel, type IAvatarModelType } from '../../../models/Avatar'
import {
AvatarTagModel,
type IAvatarTagModelType
} from '../../models/AvatarTag'
import { type ITagModelType, TagModel } from '../../models/Tag'
} from '../../../models/AvatarTag'
import { type ITagModelType, TagModel } from '../../../models/Tag'

type IAvatarsFromDB = IAvatarModelType & {
avatarTags: (IAvatarTagModelType & {
Expand All @@ -30,6 +30,7 @@ const getMarketAvatars = async (): Promise<AvatarData[]> => {

for (const avatar of avatars)
formattedResult.push({
id: avatar.id,
filename: avatar.filename,
tags: avatar.avatarTags.map((avatarTag) => avatarTag.tag.value),
isAnimated: avatar.isAnimated,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Elysia } from 'elysia'
import { AuthPlugin } from '../../services/AuthService'
import { AuthPlugin } from '../../../services/AuthService'
import getMarketAvatars from './handler'

export const MarketAvatars = new Elysia()
Expand Down
57 changes: 57 additions & 0 deletions apps/api/src/routes/market/buy/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {BalanceOperationModel} from "../../../models/BalanceOperation";
import {AvatarModel} from "../../../models/Avatar";
import {API_CODES, API_ERRORS, ApiError} from "@api";
import {UserAvatarModel} from "../../../models/UserAvatar";

type OperationStatus = {
isSuccess: boolean
currentBalance: number
}

// TODO: ВЫНЕСТИ ВСЁ КРАСИВЕНЬКО ТУТ И ТАМ

const buyAvatar = async (localUserId: bigint, strAvatarId: string): Promise<OperationStatus> => {
const avatarId = BigInt(strAvatarId)
const currentBalance = await BalanceOperationModel.sum('amount', {where: {diaryUserId: localUserId}})
const avatar = await AvatarModel.findOne({where: {id: avatarId}})

if (!avatar)
throw new ApiError(API_ERRORS.DATA_NOT_FOUND, API_CODES.NOT_FOUND)

if (currentBalance < avatar.price)
return {
isSuccess: false,
currentBalance
}

const userAvatarAlready = await UserAvatarModel.findOne({
where: {
diaryUserId: localUserId,
avatarId
}
})

if (userAvatarAlready)
return {
isSuccess: false,
currentBalance
}

await BalanceOperationModel.create({
diaryUserId: localUserId,
comment: `Покупка ${avatar.isAnimated ? 'анимированного' : 'статичного'} аватара (${avatar.id})`,
amount: -avatar.price
})

await UserAvatarModel.create({
diaryUserId: localUserId,
avatarId
})

return {
isSuccess: true,
currentBalance
}
}

export default buyAvatar
19 changes: 19 additions & 0 deletions apps/api/src/routes/market/buy/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Elysia, t } from 'elysia'
import { AuthPlugin } from '../../../services/AuthService'
import buyAvatar from './handler'

export const BuyAvatar = new Elysia()
.use(AuthPlugin)
.post('/buyAvatar', ({
Auth: {
user: {localUserId}
}, body: {
avatarId
}}) => buyAvatar(localUserId, avatarId), {
detail: {
tags: ['market']
},
body: t.Object({
avatarId: t.String()
})
})
5 changes: 5 additions & 0 deletions apps/api/src/routes/market/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './avatars'
export * from './userInfo'
export * from './userAvatars'
export * from './userSaveAvatar'
export * from './buy'
38 changes: 38 additions & 0 deletions apps/api/src/routes/market/userAvatars/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { AvatarData } from '@diary-spo/shared'
import {IUserAvatarModelType, UserAvatarModel} from "../../../models/UserAvatar";
import {AvatarModel, IAvatarModelType} from "../../../models/Avatar";
import {DiaryUserModel} from "../../../models/DiaryUser";

type IAvatarsFromDB = IUserAvatarModelType & {
avatar: IAvatarModelType
}

type UserAvatarData = AvatarData & {isActive: boolean}

const getUserAvatars = async (localUserId: bigint): Promise<UserAvatarData[]> => {
const currentUserAvatar = (await DiaryUserModel.findByPk(localUserId))?.avatarId ?? null
const userAvatars = (await UserAvatarModel.findAll({
where: {
diaryUserId: localUserId,
},
include: {
model: AvatarModel
}
})) as IAvatarsFromDB[]

const formattedResult: UserAvatarData[] = []

for (const userAvatar of userAvatars)
formattedResult.push({
id: userAvatar.avatar.id,
filename: userAvatar.avatar.filename,
tags: [],
isAnimated: userAvatar.avatar.isAnimated,
price: userAvatar.avatar.price,
isActive: currentUserAvatar === userAvatar.avatarId
})

return formattedResult
}

export default getUserAvatars
11 changes: 11 additions & 0 deletions apps/api/src/routes/market/userAvatars/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Elysia } from 'elysia'
import { AuthPlugin } from '../../../services/AuthService'
import getUserAvatars from './handler'

export const UserAvatars = new Elysia()
.use(AuthPlugin)
.get('/userAvatars', ({Auth: {user: {localUserId}}}) => getUserAvatars(localUserId), {
detail: {
tags: ['user']
}
})
40 changes: 40 additions & 0 deletions apps/api/src/routes/market/userInfo/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {DiaryUserModel} from "../../../models/DiaryUser";
import {API_CODES, API_ERRORS, ApiError} from "@api";
import {AvatarModel} from "../../../models/Avatar";
import {Nullable} from "@diary-spo/shared";
import {BalanceOperationModel} from "../../../models/BalanceOperation";

type MarketUserInfo = {
firstName: string
lastName: string
avatar: Nullable<string>
balance: number
}

const getUserInfo = async (localUserId: bigint): Promise<MarketUserInfo> => {
const diaryUser = await DiaryUserModel.findByPk(localUserId)

if (!diaryUser)
throw new ApiError(API_ERRORS.USER_NOT_FOUND, API_CODES.NOT_FOUND)

const firstName = diaryUser.firstName
const lastName = diaryUser.lastName

const avatarModel = diaryUser.avatarId ?
await AvatarModel.findByPk(diaryUser.avatarId)
: null

const avatar = avatarModel ? avatarModel.filename : null

// Если нет записей, то может вернуться что-то другое (вдруг), поэтому ставим ??
const balance = await BalanceOperationModel.sum('amount', {where: {diaryUserId: diaryUser.id}}) ?? 0

return {
firstName,
lastName,
avatar,
balance
}
}

export default getUserInfo
14 changes: 14 additions & 0 deletions apps/api/src/routes/market/userInfo/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Elysia } from 'elysia'
import { AuthPlugin } from '../../../services/AuthService'
import getUserInfo from './handler'

export const UserInfo = new Elysia()
.use(AuthPlugin)
.get('/userInfo', ({
Auth: {
user: {localUserId}
}}) => getUserInfo(localUserId), {
detail: {
tags: ['market']
}
})
34 changes: 34 additions & 0 deletions apps/api/src/routes/market/userSaveAvatar/handler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {Nullable} from "@diary-spo/shared"
import {UserAvatarModel} from "../../../models/UserAvatar";
import {API_CODES, API_ERRORS, ApiError} from "@api";
import {DiaryUserModel} from "../../../models/DiaryUser";

type MarketUserInfo = {
firstName: string
lastName: string
avatar: Nullable<string>
balance: number
}

const userSaveAvatar = async (localUserId: bigint, stringAvatarId: string): Promise<void> => {
const avatarId = BigInt(stringAvatarId)
const avatarIsSetNull = avatarId === BigInt(-1)

if (!avatarIsSetNull) {
const userAvatar = await UserAvatarModel.findOne({where: {avatarId}})

if (!userAvatar)
throw new ApiError(API_ERRORS.DATA_NOT_FOUND, API_CODES.NOT_FOUND)
}

await DiaryUserModel.update({
avatarId: avatarIsSetNull ? null : avatarId
},
{
where: {
id: localUserId
}
})
}

export default userSaveAvatar
Loading

0 comments on commit 6eb427f

Please sign in to comment.