|
@@ -0,0 +1,416 @@
|
|
|
|
|
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
|
|
|
|
|
+import { z } from '@hono/zod-openapi';
|
|
|
|
|
+import { AppDataSource, ErrorSchema, parseWithAwait } from '@d8d/shared-utils';
|
|
|
|
|
+import { authMiddleware, enterpriseAuthMiddleware } from '@d8d/auth-module';
|
|
|
|
|
+import { AuthContext } from '@d8d/shared-types';
|
|
|
|
|
+import { StatisticsService } from '../services/statistics.service';
|
|
|
|
|
+import {
|
|
|
|
|
+ DisabilityTypeDistributionResponseSchema,
|
|
|
|
|
+ GenderDistributionResponseSchema,
|
|
|
|
|
+ AgeDistributionResponseSchema,
|
|
|
|
|
+ HouseholdDistributionResponseSchema,
|
|
|
|
|
+ JobStatusDistributionResponseSchema,
|
|
|
|
|
+ SalaryDistributionResponseSchema,
|
|
|
|
|
+ StatisticsQuerySchema
|
|
|
|
|
+} from '../schemas/statistics.schema';
|
|
|
|
|
+
|
|
|
|
|
+// 获取数据源和统计服务
|
|
|
|
|
+const getStatisticsService = async () => {
|
|
|
|
|
+ return new StatisticsService(AppDataSource);
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+// 残疾类型分布统计路由
|
|
|
|
|
+const disabilityTypeDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/disability-type-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '残疾类型分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: DisabilityTypeDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 性别分布统计路由
|
|
|
|
|
+const genderDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/gender-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '性别分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: GenderDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 年龄分布统计路由
|
|
|
|
|
+const ageDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/age-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '年龄分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: AgeDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 户籍分布统计路由
|
|
|
|
|
+const householdDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/household-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '户籍分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: HouseholdDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 在职状态分布统计路由
|
|
|
|
|
+const jobStatusDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/job-status-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '在职状态分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: JobStatusDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 薪资分布统计路由
|
|
|
|
|
+const salaryDistributionRoute = createRoute({
|
|
|
|
|
+ method: 'get',
|
|
|
|
|
+ path: '/salary-distribution',
|
|
|
|
|
+ middleware: [enterpriseAuthMiddleware],
|
|
|
|
|
+ request: {
|
|
|
|
|
+ query: StatisticsQuerySchema
|
|
|
|
|
+ },
|
|
|
|
|
+ responses: {
|
|
|
|
|
+ 200: {
|
|
|
|
|
+ description: '薪资分布统计获取成功',
|
|
|
|
|
+ content: {
|
|
|
|
|
+ 'application/json': { schema: SalaryDistributionResponseSchema }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ 400: {
|
|
|
|
|
+ description: '参数错误',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 401: {
|
|
|
|
|
+ description: '认证失败或企业权限不足',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ },
|
|
|
|
|
+ 500: {
|
|
|
|
|
+ description: '获取统计信息失败',
|
|
|
|
|
+ content: { 'application/json': { schema: ErrorSchema } }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+// 创建应用实例并使用链式语法注册所有路由
|
|
|
|
|
+const app = new OpenAPIHono<AuthContext>()
|
|
|
|
|
+ // 残疾类型分布统计
|
|
|
|
|
+ .openapi(disabilityTypeDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getDisabilityTypeDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(DisabilityTypeDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取残疾类型分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ // 性别分布统计
|
|
|
|
|
+ .openapi(genderDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getGenderDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(GenderDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取性别分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ // 年龄分布统计
|
|
|
|
|
+ .openapi(ageDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getAgeDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(AgeDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取年龄分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ // 户籍分布统计
|
|
|
|
|
+ .openapi(householdDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getHouseholdDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(HouseholdDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取户籍分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ // 在职状态分布统计
|
|
|
|
|
+ .openapi(jobStatusDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getJobStatusDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(JobStatusDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取在职状态分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ // 薪资分布统计
|
|
|
|
|
+ .openapi(salaryDistributionRoute, async (c) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const user = c.get('user');
|
|
|
|
|
+ const query = c.req.valid('query');
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用查询参数中的companyId,否则使用认证用户的companyId
|
|
|
|
|
+ const targetCompanyId = query.companyId || user?.companyId;
|
|
|
|
|
+
|
|
|
|
|
+ if (!targetCompanyId) {
|
|
|
|
|
+ return c.json({ code: 400, message: '企业ID不能为空' }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const statisticsService = await getStatisticsService();
|
|
|
|
|
+ const result = await statisticsService.getSalaryDistribution(targetCompanyId);
|
|
|
|
|
+
|
|
|
|
|
+ // 使用 parseWithAwait 验证和转换数据
|
|
|
|
|
+ const validatedResult = await parseWithAwait(SalaryDistributionResponseSchema, result);
|
|
|
|
|
+ return c.json(validatedResult, 200);
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ if (error instanceof z.ZodError) {
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 400,
|
|
|
|
|
+ message: '参数错误',
|
|
|
|
|
+ errors: error.issues
|
|
|
|
|
+ }, 400);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ console.error('获取薪资分布统计失败:', error);
|
|
|
|
|
+ return c.json({
|
|
|
|
|
+ code: 500,
|
|
|
|
|
+ message: error instanceof Error ? error.message : '获取统计信息失败'
|
|
|
|
|
+ }, 500);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+// 导出应用实例
|
|
|
|
|
+export default app;
|