ballrush-core 0.6.6 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/user.d.ts +8 -0
- package/dist/domain/user.js +16 -2
- package/dist/mongo/schemas/user.schema.d.ts +2 -0
- package/dist/mongo/schemas/user.schema.js +2 -0
- package/dist/ports/index.d.ts +1 -0
- package/dist/ports/index.js +1 -0
- package/dist/ports/stat-user-context.repository.d.ts +16 -0
- package/dist/ports/stat-user-context.repository.js +2 -0
- package/dist/repositories/index.d.ts +1 -0
- package/dist/repositories/index.js +1 -0
- package/dist/repositories/mongo/stat-user-context.repository.d.ts +15 -0
- package/dist/repositories/mongo/stat-user-context.repository.js +119 -0
- package/dist/repositories/mongo/user.mapper.js +5 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/stat-user-group-seasons.types.d.ts +9 -0
- package/dist/types/stat-user-group-seasons.types.js +6 -0
- package/dist/types/stat-user-groups.types.d.ts +11 -0
- package/dist/types/stat-user-groups.types.js +6 -0
- package/package.json +1 -1
package/dist/domain/user.d.ts
CHANGED
|
@@ -4,17 +4,25 @@ export declare class User {
|
|
|
4
4
|
private readonly firstName;
|
|
5
5
|
private readonly username?;
|
|
6
6
|
private language;
|
|
7
|
+
private avatarUrl?;
|
|
8
|
+
private telegramAvatarId?;
|
|
7
9
|
private constructor();
|
|
8
10
|
static create(params: {
|
|
9
11
|
userId: number;
|
|
10
12
|
firstName: string;
|
|
11
13
|
username?: string;
|
|
12
14
|
language?: LanguageType;
|
|
15
|
+
avatarUrl?: string;
|
|
16
|
+
telegramAvatarId?: string;
|
|
13
17
|
}): User;
|
|
14
18
|
getUserId(): number;
|
|
15
19
|
getFirstName(): string;
|
|
16
20
|
getUsername(): string | undefined;
|
|
17
21
|
getLanguage(): LanguageType;
|
|
18
22
|
setLanguage(language: LanguageType): void;
|
|
23
|
+
getAvatarUrl(): string | undefined;
|
|
24
|
+
setAvatarUrl(url: string): void;
|
|
25
|
+
getTelegramAvatarId(): string | undefined;
|
|
26
|
+
setTelegramAvatarId(id: string): void;
|
|
19
27
|
getFullName(): string;
|
|
20
28
|
}
|
package/dist/domain/user.js
CHANGED
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.User = void 0;
|
|
4
4
|
class User {
|
|
5
|
-
constructor(userId, firstName, username, language = "ru") {
|
|
5
|
+
constructor(userId, firstName, username, language = "ru", avatarUrl, telegramAvatarId) {
|
|
6
6
|
this.userId = userId;
|
|
7
7
|
this.firstName = firstName;
|
|
8
8
|
this.username = username;
|
|
9
9
|
this.language = language;
|
|
10
|
+
this.avatarUrl = avatarUrl;
|
|
11
|
+
this.telegramAvatarId = telegramAvatarId;
|
|
10
12
|
}
|
|
11
13
|
static create(params) {
|
|
12
14
|
if (!params.userId) {
|
|
@@ -15,7 +17,7 @@ class User {
|
|
|
15
17
|
if (!params.firstName) {
|
|
16
18
|
throw new Error(`User creation failed: missing firstName (userId=${params.userId ?? 'N/A'})`);
|
|
17
19
|
}
|
|
18
|
-
return new User(params.userId, params.firstName, params.username, params.language ?? "ru");
|
|
20
|
+
return new User(params.userId, params.firstName, params.username, params.language ?? "ru", params.avatarUrl, params.telegramAvatarId);
|
|
19
21
|
}
|
|
20
22
|
getUserId() {
|
|
21
23
|
return this.userId;
|
|
@@ -32,6 +34,18 @@ class User {
|
|
|
32
34
|
setLanguage(language) {
|
|
33
35
|
this.language = language;
|
|
34
36
|
}
|
|
37
|
+
getAvatarUrl() {
|
|
38
|
+
return this.avatarUrl;
|
|
39
|
+
}
|
|
40
|
+
setAvatarUrl(url) {
|
|
41
|
+
this.avatarUrl = url;
|
|
42
|
+
}
|
|
43
|
+
getTelegramAvatarId() {
|
|
44
|
+
return this.telegramAvatarId;
|
|
45
|
+
}
|
|
46
|
+
setTelegramAvatarId(id) {
|
|
47
|
+
this.telegramAvatarId = id;
|
|
48
|
+
}
|
|
35
49
|
getFullName() {
|
|
36
50
|
return this.username
|
|
37
51
|
? `${this.firstName} (${this.username})`
|
|
@@ -5,6 +5,8 @@ export interface UserDoc {
|
|
|
5
5
|
language: LanguageType;
|
|
6
6
|
userId: number;
|
|
7
7
|
username?: string;
|
|
8
|
+
avatarUrl?: string;
|
|
9
|
+
telegramAvatarId?: string;
|
|
8
10
|
}
|
|
9
11
|
export declare function createUserSchema(): Schema<UserDoc, import("mongoose").Model<UserDoc, any, any, any, import("mongoose").Document<unknown, any, UserDoc> & UserDoc & {
|
|
10
12
|
_id: import("mongoose").Types.ObjectId;
|
|
@@ -8,6 +8,8 @@ function createUserSchema() {
|
|
|
8
8
|
username: { type: String },
|
|
9
9
|
userId: { type: Number, required: true, unique: true, index: true },
|
|
10
10
|
language: { type: String, enum: ["ru", "en", "ua"], default: "ru" },
|
|
11
|
+
avatarUrl: { type: String },
|
|
12
|
+
telegramAvatarId: { type: String },
|
|
11
13
|
});
|
|
12
14
|
return UserSchema;
|
|
13
15
|
}
|
package/dist/ports/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./stat-group-last-100-games.repository";
|
|
|
9
9
|
export * from "./stat-group-activity-chart.repository";
|
|
10
10
|
export * from "./stat-user-profile.repository";
|
|
11
11
|
export * from "./stat-user-match-history.repository";
|
|
12
|
+
export * from "./stat-user-context.repository";
|
|
12
13
|
export * from "./stat-event.repository";
|
|
13
14
|
export * from "./seasons.repository";
|
|
14
15
|
export * from "./achievements.repository";
|
package/dist/ports/index.js
CHANGED
|
@@ -25,6 +25,7 @@ __exportStar(require("./stat-group-last-100-games.repository"), exports);
|
|
|
25
25
|
__exportStar(require("./stat-group-activity-chart.repository"), exports);
|
|
26
26
|
__exportStar(require("./stat-user-profile.repository"), exports);
|
|
27
27
|
__exportStar(require("./stat-user-match-history.repository"), exports);
|
|
28
|
+
__exportStar(require("./stat-user-context.repository"), exports);
|
|
28
29
|
__exportStar(require("./stat-event.repository"), exports);
|
|
29
30
|
__exportStar(require("./seasons.repository"), exports);
|
|
30
31
|
__exportStar(require("./achievements.repository"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StatUserGroupItem } from "../types/stat-user-groups.types";
|
|
2
|
+
import { StatUserGroupSeasons } from "../types/stat-user-group-seasons.types";
|
|
3
|
+
/**
|
|
4
|
+
* Repository interface for user context data
|
|
5
|
+
* Provides methods to query user groups and seasons
|
|
6
|
+
*/
|
|
7
|
+
export interface StatUserContextRepository {
|
|
8
|
+
/**
|
|
9
|
+
* Список групп, в которых пользователь имеет матчи / статистику.
|
|
10
|
+
*/
|
|
11
|
+
findUserGroups(userId: number): Promise<StatUserGroupItem[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Список уникальных сезонов пользователя для конкретной группы.
|
|
14
|
+
*/
|
|
15
|
+
findUserSeasonsInGroup(userId: number, groupId: number): Promise<StatUserGroupSeasons | null>;
|
|
16
|
+
}
|
|
@@ -9,6 +9,7 @@ export * from "./mongo/stat-group-last-100-games.repository";
|
|
|
9
9
|
export * from "./mongo/stat-group-activity-chart.repository";
|
|
10
10
|
export * from "./mongo/stat-user-profile.repository";
|
|
11
11
|
export * from "./mongo/stat-user-match-history.repository";
|
|
12
|
+
export * from "./mongo/stat-user-context.repository";
|
|
12
13
|
export * from "./mongo/stat-event.repository";
|
|
13
14
|
export * from "./mongo/season.repository";
|
|
14
15
|
export * from "./mongo/achievement.repository";
|
|
@@ -25,6 +25,7 @@ __exportStar(require("./mongo/stat-group-last-100-games.repository"), exports);
|
|
|
25
25
|
__exportStar(require("./mongo/stat-group-activity-chart.repository"), exports);
|
|
26
26
|
__exportStar(require("./mongo/stat-user-profile.repository"), exports);
|
|
27
27
|
__exportStar(require("./mongo/stat-user-match-history.repository"), exports);
|
|
28
|
+
__exportStar(require("./mongo/stat-user-context.repository"), exports);
|
|
28
29
|
__exportStar(require("./mongo/stat-event.repository"), exports);
|
|
29
30
|
__exportStar(require("./mongo/season.repository"), exports);
|
|
30
31
|
__exportStar(require("./mongo/achievement.repository"), exports);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Model } from "mongoose";
|
|
2
|
+
import type { StatUserContextRepository } from "../../ports/stat-user-context.repository";
|
|
3
|
+
import type { StatUserMatchHistoryDoc } from "../../mongo/schemas/stat-user-match-history.schema";
|
|
4
|
+
import type { StatUserGroupItem } from "../../types/stat-user-groups.types";
|
|
5
|
+
import type { StatUserGroupSeasons } from "../../types/stat-user-group-seasons.types";
|
|
6
|
+
/**
|
|
7
|
+
* MongoDB implementation of StatUserContextRepository
|
|
8
|
+
* Provides methods to query user groups and seasons from match history
|
|
9
|
+
*/
|
|
10
|
+
export declare class MongoStatUserContextRepository implements StatUserContextRepository {
|
|
11
|
+
private readonly model;
|
|
12
|
+
constructor(model: Model<StatUserMatchHistoryDoc>);
|
|
13
|
+
findUserGroups(userId: number): Promise<StatUserGroupItem[]>;
|
|
14
|
+
findUserSeasonsInGroup(userId: number, groupId: number): Promise<StatUserGroupSeasons | null>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MongoStatUserContextRepository = void 0;
|
|
4
|
+
const errors_1 = require("../../errors");
|
|
5
|
+
/**
|
|
6
|
+
* MongoDB implementation of StatUserContextRepository
|
|
7
|
+
* Provides methods to query user groups and seasons from match history
|
|
8
|
+
*/
|
|
9
|
+
class MongoStatUserContextRepository {
|
|
10
|
+
constructor(model) {
|
|
11
|
+
this.model = model;
|
|
12
|
+
}
|
|
13
|
+
async findUserGroups(userId) {
|
|
14
|
+
try {
|
|
15
|
+
// Validate input
|
|
16
|
+
if (!userId || userId === 0) {
|
|
17
|
+
throw new errors_1.ValidationError('StatUserContext', 'userId', userId, 'must be a non-zero number');
|
|
18
|
+
}
|
|
19
|
+
// Aggregation pipeline to get distinct groups with statistics
|
|
20
|
+
const pipeline = [
|
|
21
|
+
// Match all matches for the user
|
|
22
|
+
{
|
|
23
|
+
$match: {
|
|
24
|
+
userId: userId,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
// Group by groupId to aggregate statistics
|
|
28
|
+
{
|
|
29
|
+
$group: {
|
|
30
|
+
_id: '$groupId',
|
|
31
|
+
totalMatches: { $sum: 1 },
|
|
32
|
+
lastMatchAt: { $max: '$date' },
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
// Lookup group name from Groups collection
|
|
36
|
+
{
|
|
37
|
+
$lookup: {
|
|
38
|
+
from: 'groups',
|
|
39
|
+
localField: '_id',
|
|
40
|
+
foreignField: 'groupId',
|
|
41
|
+
as: 'groupInfo',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
// Unwind group info (handle case where group doesn't exist)
|
|
45
|
+
{
|
|
46
|
+
$unwind: {
|
|
47
|
+
path: '$groupInfo',
|
|
48
|
+
preserveNullAndEmptyArrays: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
// Project final result
|
|
52
|
+
{
|
|
53
|
+
$project: {
|
|
54
|
+
userId: { $literal: userId },
|
|
55
|
+
groupId: '$_id',
|
|
56
|
+
groupName: {
|
|
57
|
+
$ifNull: ['$groupInfo.groupName', ''],
|
|
58
|
+
},
|
|
59
|
+
lastMatchAt: '$lastMatchAt',
|
|
60
|
+
totalMatches: '$totalMatches',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
// Sort by lastMatchAt descending (most recent first)
|
|
64
|
+
{
|
|
65
|
+
$sort: { lastMatchAt: -1 },
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
const results = await this.model.aggregate(pipeline).exec();
|
|
69
|
+
// Map to domain types
|
|
70
|
+
return results.map((item) => ({
|
|
71
|
+
userId: item.userId,
|
|
72
|
+
groupId: item.groupId,
|
|
73
|
+
groupName: item.groupName,
|
|
74
|
+
lastMatchAt: item.lastMatchAt ? new Date(item.lastMatchAt) : null,
|
|
75
|
+
totalMatches: item.totalMatches,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
if (error instanceof errors_1.ValidationError) {
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
throw new errors_1.QueryError('findUserGroups', 'StatUserContext', userId, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async findUserSeasonsInGroup(userId, groupId) {
|
|
86
|
+
try {
|
|
87
|
+
// Validate input
|
|
88
|
+
if (!userId || userId === 0) {
|
|
89
|
+
throw new errors_1.ValidationError('StatUserContext', 'userId', userId, 'must be a non-zero number');
|
|
90
|
+
}
|
|
91
|
+
if (!groupId || groupId === 0) {
|
|
92
|
+
throw new errors_1.ValidationError('StatUserContext', 'groupId', groupId, 'must be a non-zero number');
|
|
93
|
+
}
|
|
94
|
+
// Get distinct seasonIds for the user in the group
|
|
95
|
+
const seasonIds = await this.model.distinct('seasonId', {
|
|
96
|
+
userId,
|
|
97
|
+
groupId,
|
|
98
|
+
});
|
|
99
|
+
// If no seasons found, return null
|
|
100
|
+
if (!seasonIds || seasonIds.length === 0) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// Sort seasons (assuming they're strings like "Autumn 2025")
|
|
104
|
+
const sortedSeasons = seasonIds.sort();
|
|
105
|
+
return {
|
|
106
|
+
userId,
|
|
107
|
+
groupId,
|
|
108
|
+
seasons: sortedSeasons,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
if (error instanceof errors_1.ValidationError) {
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
throw new errors_1.QueryError('findUserSeasonsInGroup', 'StatUserContext', `${userId}:${groupId}`, error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
exports.MongoStatUserContextRepository = MongoStatUserContextRepository;
|
|
@@ -8,7 +8,9 @@ function toDomain(doc) {
|
|
|
8
8
|
firstName: doc.firstName,
|
|
9
9
|
username: doc.username,
|
|
10
10
|
userId: doc.userId,
|
|
11
|
-
language: doc.language
|
|
11
|
+
language: doc.language,
|
|
12
|
+
avatarUrl: doc.avatarUrl,
|
|
13
|
+
telegramAvatarId: doc.telegramAvatarId,
|
|
12
14
|
});
|
|
13
15
|
}
|
|
14
16
|
function toDoc(user) {
|
|
@@ -17,5 +19,7 @@ function toDoc(user) {
|
|
|
17
19
|
username: user.getUsername(),
|
|
18
20
|
userId: user.getUserId(),
|
|
19
21
|
language: user.getLanguage(),
|
|
22
|
+
avatarUrl: user.getAvatarUrl(),
|
|
23
|
+
telegramAvatarId: user.getTelegramAvatarId(),
|
|
20
24
|
};
|
|
21
25
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,4 +5,6 @@ export * from "./stat-group-last-100-games.types";
|
|
|
5
5
|
export * from "./stat-group-activity-chart.types";
|
|
6
6
|
export * from "./stat-user-profile.types";
|
|
7
7
|
export * from "./stat-user-match-history.types";
|
|
8
|
+
export * from "./stat-user-groups.types";
|
|
9
|
+
export * from "./stat-user-group-seasons.types";
|
|
8
10
|
export * from "./stat-event.types";
|
package/dist/types/index.js
CHANGED
|
@@ -21,4 +21,6 @@ __exportStar(require("./stat-group-last-100-games.types"), exports);
|
|
|
21
21
|
__exportStar(require("./stat-group-activity-chart.types"), exports);
|
|
22
22
|
__exportStar(require("./stat-user-profile.types"), exports);
|
|
23
23
|
__exportStar(require("./stat-user-match-history.types"), exports);
|
|
24
|
+
__exportStar(require("./stat-user-groups.types"), exports);
|
|
25
|
+
__exportStar(require("./stat-user-group-seasons.types"), exports);
|
|
24
26
|
__exportStar(require("./stat-event.types"), exports);
|