From eafd308db2e814c05a59f7359053aac11cb3218e Mon Sep 17 00:00:00 2001 From: ditiqi Date: Wed, 5 Mar 2025 21:21:03 +0800 Subject: [PATCH] add --- .../models/department/department.router.ts | 40 +-- apps/server/src/models/staff/staff.router.ts | 12 + .../src/models/staff/staff.row.service.ts | 233 +++++++++--------- apps/server/src/models/staff/staff.service.ts | 44 ++-- .../models/course/list/PostList.tsx | 1 + 5 files changed, 180 insertions(+), 150 deletions(-) diff --git a/apps/server/src/models/department/department.router.ts b/apps/server/src/models/department/department.router.ts index 2cd168c..edec18e 100755 --- a/apps/server/src/models/department/department.router.ts +++ b/apps/server/src/models/department/department.router.ts @@ -1,21 +1,29 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { DepartmentService } from './department.service'; // assuming it's in the same directory -import { DepartmentMethodSchema, Prisma, UpdateOrderSchema } from '@nice/common'; +import { + DepartmentMethodSchema, + Prisma, + UpdateOrderSchema, +} from '@nice/common'; import { z, ZodType } from 'zod'; import { DepartmentRowService } from './department.row.service'; -const DepartmentCreateArgsSchema: ZodType = z.any() -const DepartmentUpdateArgsSchema: ZodType = z.any() -const DepartmentFindFirstArgsSchema: ZodType = z.any() -const DepartmentFindManyArgsSchema: ZodType = z.any() +const DepartmentCreateArgsSchema: ZodType = + z.any(); +const DepartmentUpdateArgsSchema: ZodType = + z.any(); +const DepartmentFindFirstArgsSchema: ZodType = + z.any(); +const DepartmentFindManyArgsSchema: ZodType = + z.any(); @Injectable() export class DepartmentRouter { constructor( private readonly trpc: TrpcService, private readonly departmentService: DepartmentService, // 注入 DepartmentService - private readonly departmentRowService: DepartmentRowService - ) { } + private readonly departmentRowService: DepartmentRowService, + ) {} router = this.trpc.router({ // 创建部门 create: this.trpc.protectProcedure @@ -36,9 +44,11 @@ export class DepartmentRouter { return this.departmentService.softDeleteByIds(input.ids); }), // 更新部门顺序 - updateOrder: this.trpc.protectProcedure.input(UpdateOrderSchema).mutation(async ({ input }) => { - return this.departmentService.updateOrder(input) - }), + updateOrder: this.trpc.protectProcedure + .input(UpdateOrderSchema) + .mutation(async ({ input }) => { + return this.departmentService.updateOrder(input); + }), // 查询多个部门 findMany: this.trpc.procedure .input(DepartmentFindManyArgsSchema) // 假设 StaffMethodSchema.findMany 是根据关键字查找员工的 Zod schema @@ -53,13 +63,15 @@ export class DepartmentRouter { }), // 获取子部门的简单树结构 getChildSimpleTree: this.trpc.procedure - .input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => { - return await this.departmentService.getChildSimpleTree(input) + .input(DepartmentMethodSchema.getSimpleTree) + .query(async ({ input }) => { + return await this.departmentService.getChildSimpleTree(input); }), // 获取父部门的简单树结构 getParentSimpleTree: this.trpc.procedure - .input(DepartmentMethodSchema.getSimpleTree).query(async ({ input }) => { - return await this.departmentService.getParentSimpleTree(input) + .input(DepartmentMethodSchema.getSimpleTree) + .query(async ({ input }) => { + return await this.departmentService.getParentSimpleTree(input); }), // 获取部门行数据 getRows: this.trpc.protectProcedure diff --git a/apps/server/src/models/staff/staff.router.ts b/apps/server/src/models/staff/staff.router.ts index 4768b16..9f227a4 100755 --- a/apps/server/src/models/staff/staff.router.ts +++ b/apps/server/src/models/staff/staff.router.ts @@ -61,6 +61,18 @@ export class StaffRouter { .query(async ({ input }) => { return await this.staffService.findMany(input); }), + findManyWithPagination: this.trpc.procedure + .input( + z.object({ + page: z.number().optional(), + pageSize: z.number().optional(), + where: StaffWhereInputSchema.optional(), + select: StaffSelectSchema.optional(), + }), + ) // Assuming StaffMethodSchema.findMany is the Zod schema for finding staffs by keyword + .query(async ({ input }) => { + return await this.staffService.findManyWithPagination(input); + }), getRows: this.trpc.protectProcedure .input(StaffMethodSchema.getRows) .query(async ({ input, ctx }) => { diff --git a/apps/server/src/models/staff/staff.row.service.ts b/apps/server/src/models/staff/staff.row.service.ts index c2e92c2..7347725 100755 --- a/apps/server/src/models/staff/staff.row.service.ts +++ b/apps/server/src/models/staff/staff.row.service.ts @@ -1,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { - db, - ObjectType, - StaffMethodSchema, - UserProfile, - RolePerms, - ResPerm, - Staff, - RowModelRequest, + db, + ObjectType, + StaffMethodSchema, + UserProfile, + RolePerms, + ResPerm, + Staff, + RowModelRequest, } from '@nice/common'; import { DepartmentService } from '../department/department.service'; import { RowCacheService } from '../base/row-cache.service'; @@ -15,121 +15,116 @@ import { z } from 'zod'; import { isFieldCondition } from '../base/sql-builder'; @Injectable() export class StaffRowService extends RowCacheService { - constructor( - private readonly departmentService: DepartmentService, - ) { - super(ObjectType.STAFF, false); + constructor(private readonly departmentService: DepartmentService) { + super(ObjectType.STAFF, false); + } + createUnGroupingRowSelect(request?: RowModelRequest): string[] { + const result = super + .createUnGroupingRowSelect(request) + .concat([ + `${this.tableName}.id AS id`, + `${this.tableName}.username AS username`, + `${this.tableName}.showname AS showname`, + `${this.tableName}.avatar AS avatar`, + `${this.tableName}.officer_id AS officer_id`, + `${this.tableName}.phone_number AS phone_number`, + `${this.tableName}.order AS order`, + `${this.tableName}.enabled AS enabled`, + 'dept.name AS dept_name', + 'domain.name AS domain_name', + ]); + return result; + } + createJoinSql(request?: RowModelRequest): string[] { + return [ + `LEFT JOIN department dept ON ${this.tableName}.dept_id = dept.id`, + `LEFT JOIN department domain ON ${this.tableName}.domain_id = domain.id`, + ]; + } + protected createGetRowsFilters( + request: z.infer, + staff: UserProfile, + ) { + const condition = super.createGetRowsFilters(request); + const { domainId, includeDeleted = false } = request; + if (isFieldCondition(condition)) { + return; } - createUnGroupingRowSelect(request?: RowModelRequest): string[] { - const result = super.createUnGroupingRowSelect(request).concat([ - `${this.tableName}.id AS id`, - `${this.tableName}.username AS username`, - `${this.tableName}.showname AS showname`, - `${this.tableName}.avatar AS avatar`, - `${this.tableName}.officer_id AS officer_id`, - `${this.tableName}.phone_number AS phone_number`, - `${this.tableName}.order AS order`, - `${this.tableName}.enabled AS enabled`, - 'dept.name AS dept_name', - 'domain.name AS domain_name', - ]); - return result + if (domainId) { + condition.AND.push({ + field: `${this.tableName}.domain_id`, + value: domainId, + op: 'equals', + }); + } else { + condition.AND.push({ + field: `${this.tableName}.domain_id`, + op: 'blank', + }); } - createJoinSql(request?: RowModelRequest): string[] { - return [ - `LEFT JOIN department dept ON ${this.tableName}.dept_id = dept.id`, - `LEFT JOIN department domain ON ${this.tableName}.domain_id = domain.id`, - ]; + if (!includeDeleted) { + condition.AND.push({ + field: `${this.tableName}.deleted_at`, + type: 'date', + op: 'blank', + }); } - protected createGetRowsFilters( - request: z.infer, - staff: UserProfile, - ) { - const condition = super.createGetRowsFilters(request); - const { domainId, includeDeleted = false } = request; - if (isFieldCondition(condition)) { - return; - } - if (domainId) { - condition.AND.push({ - field: `${this.tableName}.domain_id`, - value: domainId, - op: 'equals', - }); - } else { - condition.AND.push({ - field: `${this.tableName}.domain_id`, - op: 'blank', - }); - } - if (!includeDeleted) { - condition.AND.push({ - field: `${this.tableName}.deleted_at`, - type: 'date', - op: 'blank', - }); - } - condition.OR = []; - if (!staff.permissions.includes(RolePerms.MANAGE_ANY_STAFF)) { - if (staff.permissions.includes(RolePerms.MANAGE_DOM_STAFF)) { - condition.OR.push({ - field: 'dept.id', - value: staff.domainId, - op: 'equals', - }); - } - } - - return condition; - } - - async getPermissionContext(id: string, staff: UserProfile) { - const data = await db.staff.findUnique({ - where: { id }, - select: { - deptId: true, - domainId: true, - }, + condition.OR = []; + if (!staff.permissions.includes(RolePerms.MANAGE_ANY_STAFF)) { + if (staff.permissions.includes(RolePerms.MANAGE_DOM_STAFF)) { + condition.OR.push({ + field: 'dept.id', + value: staff.domainId, + op: 'equals', }); - const deptId = data?.deptId; - const isFromSameDept = staff.deptIds?.includes(deptId); - const domainChildDeptIds = await this.departmentService.getDescendantIds( - staff.domainId, true - ); - const belongsToDomain = domainChildDeptIds.includes( - deptId, - ); - return { isFromSameDept, belongsToDomain }; - } - protected async setResPermissions( - data: Staff, - staff: UserProfile, - ) { - const permissions: ResPerm = {}; - const { isFromSameDept, belongsToDomain } = await this.getPermissionContext( - data.id, - staff, - ); - const setManagePermissions = (permissions: ResPerm) => { - Object.assign(permissions, { - read: true, - delete: true, - edit: true, - }); - }; - staff.permissions.forEach((permission) => { - switch (permission) { - case RolePerms.MANAGE_ANY_STAFF: - setManagePermissions(permissions); - break; - case RolePerms.MANAGE_DOM_STAFF: - if (belongsToDomain) { - setManagePermissions(permissions); - } - break; - } - }); - return { ...data, perm: permissions }; + } } + return condition; + } + + async getPermissionContext(id: string, staff: UserProfile) { + const data = await db.staff.findUnique({ + where: { id }, + select: { + deptId: true, + domainId: true, + }, + }); + const deptId = data?.deptId; + const isFromSameDept = staff.deptIds?.includes(deptId); + const domainChildDeptIds = await this.departmentService.getDescendantIds( + staff.domainId, + true, + ); + const belongsToDomain = domainChildDeptIds.includes(deptId); + return { isFromSameDept, belongsToDomain }; + } + protected async setResPermissions(data: Staff, staff: UserProfile) { + const permissions: ResPerm = {}; + const { isFromSameDept, belongsToDomain } = await this.getPermissionContext( + data.id, + staff, + ); + const setManagePermissions = (permissions: ResPerm) => { + Object.assign(permissions, { + read: true, + delete: true, + edit: true, + }); + }; + staff.permissions.forEach((permission) => { + switch (permission) { + case RolePerms.MANAGE_ANY_STAFF: + setManagePermissions(permissions); + break; + case RolePerms.MANAGE_DOM_STAFF: + if (belongsToDomain) { + setManagePermissions(permissions); + } + break; + } + }); + return { ...data, perm: permissions }; + } } diff --git a/apps/server/src/models/staff/staff.service.ts b/apps/server/src/models/staff/staff.service.ts index cf37549..57cddd2 100755 --- a/apps/server/src/models/staff/staff.service.ts +++ b/apps/server/src/models/staff/staff.service.ts @@ -14,7 +14,6 @@ import EventBus, { CrudOperation } from '@server/utils/event-bus'; @Injectable() export class StaffService extends BaseService { - constructor(private readonly departmentService: DepartmentService) { super(db, ObjectType.STAFF, true); } @@ -25,7 +24,10 @@ export class StaffService extends BaseService { */ async findByDept(data: z.infer) { const { deptId, domainId } = data; - const childDepts = await this.departmentService.getDescendantIds(deptId, true); + const childDepts = await this.departmentService.getDescendantIds( + deptId, + true, + ); const result = await db.staff.findMany({ where: { deptId: { in: childDepts }, @@ -50,7 +52,9 @@ export class StaffService extends BaseService { await this.validateUniqueFields(data, where.id); const updateData = { ...data, - ...(data.password && { password: await argon2.hash(data.password as string) }) + ...(data.password && { + password: await argon2.hash(data.password as string), + }), }; const result = await super.update({ ...args, data: updateData }); this.emitDataChangedEvent(result, CrudOperation.UPDATED); @@ -58,17 +62,26 @@ export class StaffService extends BaseService { } private async validateUniqueFields(data: any, excludeId?: string) { const uniqueFields = [ - { field: 'officerId', errorMsg: (val: string) => `证件号为${val}的用户已存在` }, - { field: 'phoneNumber', errorMsg: (val: string) => `手机号为${val}的用户已存在` }, - { field: 'username', errorMsg: (val: string) => `帐号为${val}的用户已存在` } + { + field: 'officerId', + errorMsg: (val: string) => `证件号为${val}的用户已存在`, + }, + { + field: 'phoneNumber', + errorMsg: (val: string) => `手机号为${val}的用户已存在`, + }, + { + field: 'username', + errorMsg: (val: string) => `帐号为${val}的用户已存在`, + }, ]; for (const { field, errorMsg } of uniqueFields) { if (data[field]) { const count = await db.staff.count({ where: { [field]: data[field], - ...(excludeId && { id: { not: excludeId } }) - } + ...(excludeId && { id: { not: excludeId } }), + }, }); if (count > 0) { throw new Error(errorMsg(data[field])); @@ -77,9 +90,8 @@ export class StaffService extends BaseService { } } - private emitDataChangedEvent(data: any, operation: CrudOperation) { - EventBus.emit("dataChanged", { + EventBus.emit('dataChanged', { type: this.objectType, operation, data, @@ -87,12 +99,12 @@ export class StaffService extends BaseService { } /** - * 更新员工DomainId - * @param data 包含domainId对象 - * @returns 更新后的员工记录 - */ + * 更新员工DomainId + * @param data 包含domainId对象 + * @returns 更新后的员工记录 + */ async updateUserDomain(data: { domainId?: string }, staff?: UserProfile) { - let { domainId } = data; + const { domainId } = data; if (staff.domainId !== domainId) { const result = await this.update({ where: { id: staff.id }, @@ -107,7 +119,6 @@ export class StaffService extends BaseService { } } - // /** // * 根据关键词或ID集合查找员工 // * @param data 包含关键词、域ID和ID集合的对象 @@ -176,5 +187,4 @@ export class StaffService extends BaseService { // return combinedResults; // } - } diff --git a/apps/web/src/components/models/course/list/PostList.tsx b/apps/web/src/components/models/course/list/PostList.tsx index ce9da18..a276060 100755 --- a/apps/web/src/components/models/course/list/PostList.tsx +++ b/apps/web/src/components/models/course/list/PostList.tsx @@ -28,6 +28,7 @@ export default function PostList({ renderItem, }: PostListProps) { const [currentPage, setCurrentPage] = useState(params?.page || 1); + const { data, isLoading }: PostPagnationProps = api.post.findManyWithPagination.useQuery({ select: courseDetailSelect,