From 286b90511b2b5ebbfa6f4d4722ba4b3f7a22f7e3 Mon Sep 17 00:00:00 2001 From: ditiqi Date: Tue, 25 Feb 2025 08:25:54 +0800 Subject: [PATCH] add --- .../queue/models/post/post.queue.service.ts | 16 ++ apps/server/src/queue/models/post/utils.ts | 46 +++- apps/server/src/queue/types.ts | 1 + apps/server/src/queue/worker/processor.ts | 8 +- apps/server/src/utils/event-bus.ts | 3 + apps/web/src/app/admin/base-setting/page.tsx | 7 + apps/web/src/app/main/layout/MainHeader.tsx | 23 +- apps/web/src/app/main/layout/UserMenu.tsx | 70 ----- .../main/layout/UserMenu/UserEditModal.tsx | 27 ++ .../src/app/main/layout/UserMenu/UserForm.tsx | 170 ++++++++++++ .../src/app/main/layout/UserMenu/UserMenu.tsx | 252 ++++++++++++++++++ .../web/src/app/main/layout/UserMenu/types.ts | 5 + .../course/detail/CourseDetailContext.tsx | 1 + .../CourseDetailHeader/CourseDetailHeader.tsx | 2 +- packages/common/src/models/select.ts | 23 +- packages/common/src/models/staff.ts | 46 ++-- packages/common/src/types.ts | 1 + 17 files changed, 578 insertions(+), 123 deletions(-) delete mode 100755 apps/web/src/app/main/layout/UserMenu.tsx create mode 100644 apps/web/src/app/main/layout/UserMenu/UserEditModal.tsx create mode 100644 apps/web/src/app/main/layout/UserMenu/UserForm.tsx create mode 100755 apps/web/src/app/main/layout/UserMenu/UserMenu.tsx create mode 100644 apps/web/src/app/main/layout/UserMenu/types.ts diff --git a/apps/server/src/queue/models/post/post.queue.service.ts b/apps/server/src/queue/models/post/post.queue.service.ts index f7370df..8e49a81 100644 --- a/apps/server/src/queue/models/post/post.queue.service.ts +++ b/apps/server/src/queue/models/post/post.queue.service.ts @@ -22,6 +22,12 @@ export class PostQueueService implements OnModuleInit { EventBus.on('updatePostState', ({ id }) => { this.addUpdatePostState({ id }); }); + EventBus.on('updatePostState', ({ id }) => { + this.addUpdatePostState({ id }); + }); + EventBus.on('updateTotalCourseViewCount', ({ visitType }) => { + this.addUpdateTotalCourseViewCount({ visitType }); + }); } async addUpdateVisitCountJob(data: updateVisitCountJobData) { this.logger.log(`update post view count ${data.id}`); @@ -37,4 +43,14 @@ export class PostQueueService implements OnModuleInit { debounce: { id: `${QueueJobType.UPDATE_POST_STATE}_${data.id}` }, }); } + async addUpdateTotalCourseViewCount({ visitType }) { + this.logger.log(`update post state ${visitType}`); + await this.generalQueue.add( + QueueJobType.UPDATE_TOTAL_COURSE_VIEW_COUNT, + { type: visitType }, + { + debounce: { id: `${QueueJobType.UPDATE_POST_STATE}_${visitType}` }, + }, + ); + } } diff --git a/apps/server/src/queue/models/post/utils.ts b/apps/server/src/queue/models/post/utils.ts index 98eb43c..115c323 100644 --- a/apps/server/src/queue/models/post/utils.ts +++ b/apps/server/src/queue/models/post/utils.ts @@ -1,4 +1,48 @@ -import { db, VisitType } from '@nice/common'; +import { + AppConfigSlug, + BaseSetting, + db, + PostType, + TaxonomySlug, + VisitType, +} from '@nice/common'; +export async function updateTotalCourseViewCount(type: VisitType) { + const courses = await db.post.findMany({ + where: { type: PostType.COURSE }, + select: { id: true }, + }); + const courseIds = courses.map((course) => course.id); + const totalViews = await db.visit.aggregate({ + _sum: { + views: true, + }, + where: { + postId: { in: courseIds }, + type: type, + }, + }); + const appConfig = await db.appConfig.findFirst({ + where: { + slug: AppConfigSlug.BASE_SETTING, + }, + select: { + id: true, + meta: true, + }, + }); + const baseSeting = appConfig.meta as BaseSetting; + await db.appConfig.update({ + where: { + slug: AppConfigSlug.BASE_SETTING, + }, + data: { + meta: { + ...baseSeting, + reads: totalViews, + }, + }, + }); +} export async function updatePostViewCount(id: string, type: VisitType) { const post = await db.post.findFirst({ where: { id }, diff --git a/apps/server/src/queue/types.ts b/apps/server/src/queue/types.ts index 7e0f308..11119ee 100755 --- a/apps/server/src/queue/types.ts +++ b/apps/server/src/queue/types.ts @@ -4,6 +4,7 @@ export enum QueueJobType { FILE_PROCESS = 'file_process', UPDATE_POST_VISIT_COUNT = 'updatePostVisitCount', UPDATE_POST_STATE = 'updatePostState', + UPDATE_TOTAL_COURSE_VIEW_COUNT = 'updateTotalCourseViewCount', } export type updateVisitCountJobData = { id: string; diff --git a/apps/server/src/queue/worker/processor.ts b/apps/server/src/queue/worker/processor.ts index a968179..86d428b 100755 --- a/apps/server/src/queue/worker/processor.ts +++ b/apps/server/src/queue/worker/processor.ts @@ -11,7 +11,10 @@ import { updateCourseReviewStats, updateParentLectureStats, } from '@server/models/post/utils'; -import { updatePostViewCount } from '../models/post/utils'; +import { + updatePostViewCount, + updateTotalCourseViewCount, +} from '../models/post/utils'; const logger = new Logger('QueueWorker'); export default async function processJob(job: Job) { try { @@ -51,6 +54,9 @@ export default async function processJob(job: Job) { if (job.name === QueueJobType.UPDATE_POST_STATE) { await updatePostViewCount(job.data.id, job.data.type); } + if (job.name === QueueJobType.UPDATE_TOTAL_COURSE_VIEW_COUNT) { + await updateTotalCourseViewCount(job.data.type); + } } catch (error: any) { logger.error( `Error processing stats update job: ${error.message}`, diff --git a/apps/server/src/utils/event-bus.ts b/apps/server/src/utils/event-bus.ts index dfb3409..3f96688 100755 --- a/apps/server/src/utils/event-bus.ts +++ b/apps/server/src/utils/event-bus.ts @@ -21,6 +21,9 @@ type Events = { updatePostState: { id: string; }; + updateTotalCourseViewCount: { + visitType: VisitType | string; + }; onMessageCreated: { data: Partial }; dataChanged: { type: string; operation: CrudOperation; data: any }; }; diff --git a/apps/web/src/app/admin/base-setting/page.tsx b/apps/web/src/app/admin/base-setting/page.tsx index 82601ee..1155868 100755 --- a/apps/web/src/app/admin/base-setting/page.tsx +++ b/apps/web/src/app/admin/base-setting/page.tsx @@ -116,6 +116,13 @@ export default function BaseSettingPage() { +
+ + + +
{/*
setSearchValue(e.target.value)} - onPressEnter={(e)=>{ + onPressEnter={(e) => { //console.log(e) - setSearchValue('') - navigate(`/courses/?searchValue=${searchValue}`) + setSearchValue(""); + navigate( + `/courses/?searchValue=${searchValue}` + ); window.scrollTo({ top: 0, behavior: "smooth" }); }} /> @@ -54,18 +56,7 @@ export function MainHeader() { )} {isAuthenticated ? ( - } - trigger={["click"]} - placement="bottomRight"> - - {(user?.showname || - user?.username || - "")[0]?.toUpperCase()} - - + ) : ( + ))} +
+ + )} + + + + + ); +} diff --git a/apps/web/src/app/main/layout/UserMenu/types.ts b/apps/web/src/app/main/layout/UserMenu/types.ts new file mode 100644 index 0000000..9809554 --- /dev/null +++ b/apps/web/src/app/main/layout/UserMenu/types.ts @@ -0,0 +1,5 @@ +export interface MenuItemType { + icon: JSX.Element; + label: string; + action: () => void; +} diff --git a/apps/web/src/components/models/course/detail/CourseDetailContext.tsx b/apps/web/src/components/models/course/detail/CourseDetailContext.tsx index 890a331..8702d19 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailContext.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailContext.tsx @@ -58,6 +58,7 @@ export function CourseDetailProvider({ ); useEffect(() => { if (course) { + console.log("read"); read.mutateAsync({ data: { visitorId: user?.id || null, diff --git a/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader.tsx b/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader.tsx index 71ec0e0..554e5b7 100755 --- a/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader.tsx +++ b/apps/web/src/components/models/course/detail/CourseDetailHeader/CourseDetailHeader.tsx @@ -8,7 +8,7 @@ import { } from "@ant-design/icons"; import { useAuth } from "@web/src/providers/auth-provider"; import { useNavigate } from "react-router-dom"; -import { UserMenu } from "@web/src/app/main/layout/UserMenu"; +import { UserMenu } from "@web/src/app/main/layout/UserMenu/UserMenu"; import { CourseDetailContext } from "../CourseDetailContext"; const { Header } = Layout; diff --git a/packages/common/src/models/select.ts b/packages/common/src/models/select.ts index 36e072d..a591134 100755 --- a/packages/common/src/models/select.ts +++ b/packages/common/src/models/select.ts @@ -70,21 +70,22 @@ export const courseDetailSelect: Prisma.PostSelect = { title: true, subTitle: true, content: true, + depts: true, // isFeatured: true, createdAt: true, updatedAt: true, // 关联表选择 - terms:{ - select:{ - id:true, - name:true, - taxonomy:{ - select:{ - id:true, - slug:true - } - } - } + terms: { + select: { + id: true, + name: true, + taxonomy: { + select: { + id: true, + slug: true, + }, + }, + }, }, enrollments: { select: { diff --git a/packages/common/src/models/staff.ts b/packages/common/src/models/staff.ts index a6ace02..565f847 100755 --- a/packages/common/src/models/staff.ts +++ b/packages/common/src/models/staff.ts @@ -2,37 +2,37 @@ import { Staff, Department } from "@prisma/client"; import { RolePerms } from "../enum"; export type StaffRowModel = { - avatar: string; - dept_name: string; - officer_id: string; - phone_number: string; - showname: string; - username: string; + avatar: string; + dept_name: string; + officer_id: string; + phone_number: string; + showname: string; + username: string; }; export type UserProfile = Staff & { - permissions: RolePerms[]; - deptIds: string[]; - parentDeptIds: string[]; - domain: Department; - department: Department; + permissions: RolePerms[]; + deptIds: string[]; + parentDeptIds: string[]; + domain: Department; + department: Department; }; export type StaffDto = Staff & { - domain?: Department; - department?: Department; + domain?: Department; + department?: Department; }; export interface AuthDto { - token: string; - staff: StaffDto; - refreshToken: string; - perms: string[]; + token: string; + staff: StaffDto; + refreshToken: string; + perms: string[]; } export interface JwtPayload { - sub: string; - username: string; + sub: string; + username: string; } export interface TokenPayload { - id: string; - phoneNumber: string; - name: string; -} \ No newline at end of file + id: string; + phoneNumber: string; + name: string; +} diff --git a/packages/common/src/types.ts b/packages/common/src/types.ts index 2d6a2ef..2a081c0 100755 --- a/packages/common/src/types.ts +++ b/packages/common/src/types.ts @@ -44,6 +44,7 @@ export interface BaseSetting { splashScreen?: string; devDept?: string; slides?: []; + reads?: number; }; } export type RowModelResult = {