import { Injectable } from "@nestjs/common"; import { BaseService } from "../base/base.service"; import { db, ObjectType, Prisma, UserProfile } from "@nice/common"; import EventBus, { CrudOperation } from "@server/utils/event-bus"; import { DefaultArgs } from "@prisma/client/runtime/library"; interface AgeRange { start: number | null; end: number | null; label: string; } interface Record { [key: number]: number[]; } interface ScoreStandard { ageRanges: AgeRange[]; scoreTable: Record; } @Injectable() export class SportStandardService extends BaseService { constructor() { super(db, ObjectType.SPORT_STANDARD, false); } async create(args: Prisma.SportStandardCreateArgs) { console.log(args) const result = await super.create(args) this.emitDataChanged(CrudOperation.CREATED, result) return result } async update(args: Prisma.SportStandardUpdateArgs) { const result = await super.update(args) this.emitDataChanged(CrudOperation.UPDATED, result) return result } async findMany(args: Prisma.SportStandardFindManyArgs) { const result = await super.findMany(args); return result; } async findUnique(args: { select?: Prisma.SportStandardSelect; include?: Prisma.SportStandardInclude; where: Prisma.SportStandardWhereUniqueInput; }): Promise<{ id: string; projectId: string; gender: boolean; personType: string; ageRanges: Prisma.JsonValue; scoreTable: Prisma.JsonValue; }> { const result = await super.findUnique(args) return result } private emitDataChanged(operation: CrudOperation, data: any) { EventBus.emit('dataChanged', { type: ObjectType.SPORT_STANDARD, operation, data, }); } async createStandard( data: { projectId: string; gender: boolean; personType: string; ageRanges: AgeRange[]; scoreTable: Record; }, select?: Prisma.SportStandardSelect, include?: Prisma.SportStandardInclude ) { console.log(data) this.validateAgeRanges(data.ageRanges); this.validateScoreTable(data.scoreTable, data.ageRanges.length); const result = await super.create({ data: { projectId: data.projectId, gender: data.gender, personType: data.personType, ageRanges: JSON.stringify(data.ageRanges), scoreTable: JSON.stringify(data.scoreTable) }, select, include }) this.emitDataChanged(CrudOperation.CREATED, result) return result } private validateAgeRanges(ranges: AgeRange[]) { // 检查年龄段是否按顺序排列且无重叠 for (let i = 0; i < ranges.length - 1; i++) { const current = ranges[i]; const next = ranges[i + 1]; if (current.end !== next.start) { throw new Error('年龄段必须连续且不重叠'); } } } private validateScoreTable( scoreTable: Record, expectedLength: number ) { Object.values(scoreTable).forEach(standards => { if (standards.length !== expectedLength) { throw new Error('分数表的每行数据长度必须与年龄段数量匹配'); } }); } public SportScoreCalculator(performance: number, age: number, scoreStandard: ScoreStandard): number { // 1. 找到对应的年龄段索引 const ageRangeIndex = scoreStandard.ageRanges.findIndex(range => { const isAboveStart = range.start === null || age > range.start; const isBelowEnd = range.end === null || age <= range.end; return isAboveStart && isBelowEnd; }); if (ageRangeIndex === -1) { throw new Error('未找到匹配的年龄段'); } // 2. 查找对应分数 const scores = Object.keys(scoreStandard.scoreTable) .map(Number) .sort((a, b) => b - a); for (const score of scores) { if (performance >= scoreStandard.scoreTable[score][ageRangeIndex]) { return score; } } return 0; } async getScore(data: { id: string; projectId: string; gender: boolean; age: number; performance: number; personType: string; }) { const standard = await this.findUnique({ where: { id: data.id, projectId: data.projectId, gender: data.gender, personType: data.personType } }) if (!standard) { throw new Error('未找到对应的评分标准'); } return this.SportScoreCalculator( data.performance, data.age, standard.scoreTable as any as ScoreStandard ) } }