order.schema.ts 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794
  1. import { z } from '@hono/zod-openapi';
  2. import { OrderStatus, WorkStatus } from '@d8d/allin-enums';
  3. import { DisabledPersonSchema } from '@d8d/allin-disability-module/schemas';
  4. // 资产类型枚举 - 从实体移到schema,供前端使用
  5. export enum AssetType {
  6. TAX = 'tax',
  7. SALARY = 'salary',
  8. JOB_RESULT = 'job_result',
  9. CONTRACT_SIGN = 'contract_sign',
  10. DISABILITY_CERT = 'disability_cert',
  11. OTHER = 'other',
  12. SALARY_VIDEO = 'salary_video',
  13. TAX_VIDEO = 'tax_video',
  14. CHECKIN_VIDEO = 'checkin_video',
  15. WORK_VIDEO = 'work_video',
  16. }
  17. // 资产文件类型枚举 - 从实体移到schema,供前端使用
  18. export enum AssetFileType {
  19. IMAGE = 'image',
  20. VIDEO = 'video',
  21. }
  22. // 视频审核状态枚举
  23. export enum AssetStatus {
  24. PENDING = 'pending',
  25. VERIFIED = 'verified',
  26. REJECTED = 'rejected'
  27. }
  28. // 用工订单实体Schema
  29. export const EmploymentOrderSchema = z.object({
  30. id: z.number().int().positive().openapi({
  31. description: '订单ID',
  32. example: 1
  33. }),
  34. orderName: z.string().max(50).nullable().optional().openapi({
  35. description: '订单名称',
  36. example: '2024年Q1用工订单'
  37. }),
  38. platformId: z.number().int().positive().openapi({
  39. description: '用人平台ID',
  40. example: 1
  41. }),
  42. companyId: z.number().int().positive().openapi({
  43. description: '用人单位ID',
  44. example: 1
  45. }),
  46. channelId: z.number().int().positive().nullable().optional().openapi({
  47. description: '渠道ID',
  48. example: 1
  49. }),
  50. expectedStartDate: z.coerce.date().nullable().optional().openapi({
  51. description: '预计开始日期',
  52. example: '2024-01-01T00:00:00Z'
  53. }),
  54. actualStartDate: z.coerce.date().nullable().optional().openapi({
  55. description: '实际开始日期',
  56. example: '2024-01-01T00:00:00Z'
  57. }),
  58. actualEndDate: z.coerce.date().nullable().optional().openapi({
  59. description: '实际结束日期',
  60. example: '2024-12-31T00:00:00Z'
  61. }),
  62. orderStatus: z.nativeEnum(OrderStatus).default(OrderStatus.DRAFT).openapi({
  63. description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
  64. example: OrderStatus.DRAFT
  65. }),
  66. workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).openapi({
  67. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  68. example: WorkStatus.NOT_WORKING
  69. }),
  70. createTime: z.coerce.date().openapi({
  71. description: '创建时间',
  72. example: '2024-01-01T00:00:00Z'
  73. }),
  74. updateTime: z.coerce.date().openapi({
  75. description: '更新时间',
  76. example: '2024-01-01T00:00:00Z'
  77. }),
  78. orderPersons: z.array(z.lazy(() => OrderPersonSchema)).optional().openapi({
  79. description: '订单关联人员列表',
  80. example: []
  81. })
  82. });
  83. // 创建用工订单DTO
  84. export const CreateEmploymentOrderSchema = z.object({
  85. orderName: z.string().max(50).optional().openapi({
  86. description: '订单名称',
  87. example: '2024年Q1用工订单'
  88. }),
  89. platformId: z.coerce.number().int().positive().openapi({
  90. description: '用人平台ID',
  91. example: 1
  92. }),
  93. companyId: z.coerce.number().int().positive().openapi({
  94. description: '用人单位ID',
  95. example: 1
  96. }),
  97. channelId: z.coerce.number().int().positive().optional().openapi({
  98. description: '渠道ID',
  99. example: 1
  100. }),
  101. expectedStartDate: z.coerce.date().optional().openapi({
  102. description: '预计开始日期',
  103. example: '2024-01-01T00:00:00Z'
  104. }),
  105. actualStartDate: z.coerce.date().optional().openapi({
  106. description: '实际开始日期',
  107. example: '2024-01-01T00:00:00Z'
  108. }),
  109. actualEndDate: z.coerce.date().optional().openapi({
  110. description: '实际结束日期',
  111. example: '2024-12-31T00:00:00Z'
  112. }),
  113. orderStatus: z.nativeEnum(OrderStatus).default(OrderStatus.DRAFT).optional().openapi({
  114. description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
  115. example: OrderStatus.DRAFT
  116. }),
  117. workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).optional().openapi({
  118. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  119. example: WorkStatus.NOT_WORKING
  120. })
  121. });
  122. // 更新用工订单DTO
  123. export const UpdateEmploymentOrderSchema = z.object({
  124. id: z.coerce.number().int().positive().openapi({
  125. description: '订单ID',
  126. example: 1
  127. }),
  128. orderName: z.string().max(50).optional().openapi({
  129. description: '订单名称',
  130. example: '2024年Q1用工订单'
  131. }),
  132. platformId: z.coerce.number().int().positive().optional().openapi({
  133. description: '用人平台ID',
  134. example: 1
  135. }),
  136. companyId: z.coerce.number().int().positive().optional().openapi({
  137. description: '用人单位ID',
  138. example: 1
  139. }),
  140. channelId: z.coerce.number().int().positive().optional().openapi({
  141. description: '渠道ID',
  142. example: 1
  143. }),
  144. expectedStartDate: z.coerce.date().optional().openapi({
  145. description: '预计开始日期',
  146. example: '2024-01-01T00:00:00Z'
  147. }),
  148. actualStartDate: z.coerce.date().optional().openapi({
  149. description: '实际开始日期',
  150. example: '2024-01-01T00:00:00Z'
  151. }),
  152. actualEndDate: z.coerce.date().optional().openapi({
  153. description: '实际结束日期',
  154. example: '2024-12-31T00:00:00Z'
  155. }),
  156. orderStatus: z.nativeEnum(OrderStatus).optional().openapi({
  157. description: '订单状态:draft-草稿, confirmed-已确认, in_progress-进行中, completed-已完成, cancelled-已取消',
  158. example: OrderStatus.DRAFT
  159. }),
  160. workStatus: z.nativeEnum(WorkStatus).optional().openapi({
  161. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  162. example: WorkStatus.NOT_WORKING
  163. })
  164. });
  165. // 订单人员关联实体Schema
  166. export const OrderPersonSchema = z.object({
  167. id: z.number().int().positive().openapi({
  168. description: '关联ID',
  169. example: 1
  170. }),
  171. orderId: z.number().int().positive().openapi({
  172. description: '订单ID',
  173. example: 1
  174. }),
  175. personId: z.number().int().positive().openapi({
  176. description: '残疾人ID',
  177. example: 1
  178. }),
  179. joinDate: z.coerce.date().openapi({
  180. description: '入职日期',
  181. example: '2024-01-01T00:00:00Z'
  182. }),
  183. actualStartDate: z.coerce.date().nullable().optional().openapi({
  184. description: '实际入职日期',
  185. example: '2024-01-15T00:00:00Z'
  186. }),
  187. leaveDate: z.coerce.date().nullable().optional().openapi({
  188. description: '离职日期',
  189. example: '2024-12-31T00:00:00Z'
  190. }),
  191. workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).openapi({
  192. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  193. example: WorkStatus.NOT_WORKING
  194. }),
  195. salaryDetail: z.coerce.number().positive().openapi({
  196. description: '个人薪资',
  197. example: 5000.00
  198. }),
  199. // 残疾人员的详细信息
  200. person: DisabledPersonSchema.pick({
  201. id: true,
  202. name: true,
  203. gender: true,
  204. disabilityType: true,
  205. phone: true,
  206. disabilityId: true
  207. }).partial().nullable().optional().openapi({
  208. description: '残疾人员详细信息'
  209. })
  210. });
  211. // 创建订单人员关联DTO
  212. export const CreateOrderPersonSchema = z.object({
  213. orderId: z.coerce.number().int().positive().openapi({
  214. description: '订单ID',
  215. example: 1
  216. }),
  217. personId: z.coerce.number().int().positive().openapi({
  218. description: '残疾人ID',
  219. example: 1
  220. }),
  221. joinDate: z.coerce.date().openapi({
  222. description: '入职日期',
  223. example: '2024-01-01T00:00:00Z'
  224. }),
  225. leaveDate: z.coerce.date().optional().openapi({
  226. description: '离职日期',
  227. example: '2024-12-31T00:00:00Z'
  228. }),
  229. workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).optional().openapi({
  230. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  231. example: WorkStatus.NOT_WORKING
  232. }),
  233. salaryDetail: z.coerce.number().positive().openapi({
  234. description: '个人薪资',
  235. example: 5000.00
  236. })
  237. });
  238. // 批量添加人员DTO(不需要orderId,从URL参数获取)
  239. export const BatchAddPersonItemSchema = z.object({
  240. personId: z.coerce.number().int().positive().openapi({
  241. description: '残疾人ID',
  242. example: 1
  243. }),
  244. joinDate: z.coerce.date().openapi({
  245. description: '入职日期',
  246. example: '2024-01-01T00:00:00Z'
  247. }),
  248. leaveDate: z.coerce.date().optional().openapi({
  249. description: '离职日期',
  250. example: '2024-12-31T00:00:00Z'
  251. }),
  252. workStatus: z.nativeEnum(WorkStatus).default(WorkStatus.NOT_WORKING).optional().openapi({
  253. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  254. example: WorkStatus.NOT_WORKING
  255. }),
  256. salaryDetail: z.coerce.number().positive().openapi({
  257. description: '个人薪资',
  258. example: 5000.00
  259. })
  260. });
  261. // 订单人员资产实体Schema
  262. export const OrderPersonAssetSchema = z.object({
  263. id: z.number().int().positive().openapi({
  264. description: '关联ID',
  265. example: 1
  266. }),
  267. orderId: z.number().int().positive().openapi({
  268. description: '订单ID',
  269. example: 1
  270. }),
  271. personId: z.number().int().positive().openapi({
  272. description: '残疾人ID',
  273. example: 1
  274. }),
  275. assetType: z.nativeEnum(AssetType).openapi({
  276. description: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他, salary_video-工资视频, tax_video-个税视频, checkin_video-打卡视频, work_video-工作视频',
  277. example: AssetType.SALARY
  278. }),
  279. assetFileType: z.nativeEnum(AssetFileType).openapi({
  280. description: '资产文件类型:image-图片, video-视频',
  281. example: AssetFileType.IMAGE
  282. }),
  283. status: z.nativeEnum(AssetStatus).optional().openapi({
  284. description: '视频审核状态:pending-待审核, verified-已验证, rejected-已拒绝',
  285. example: AssetStatus.PENDING
  286. }),
  287. fileId: z.number().int().positive().openapi({
  288. description: '文件ID,引用files表',
  289. example: 1
  290. }),
  291. relatedTime: z.coerce.date().openapi({
  292. description: '关联时间',
  293. example: '2024-01-01T00:00:00Z'
  294. }),
  295. updateTime: z.coerce.date().openapi({
  296. description: '更新时间',
  297. example: '2024-01-01T00:00:00Z'
  298. })
  299. });
  300. // 创建订单人员资产DTO
  301. export const CreateOrderPersonAssetSchema = z.object({
  302. orderId: z.coerce.number().int().positive().openapi({
  303. description: '订单ID',
  304. example: 1
  305. }),
  306. personId: z.coerce.number().int().positive().openapi({
  307. description: '残疾人ID',
  308. example: 1
  309. }),
  310. assetType: z.nativeEnum(AssetType).openapi({
  311. description: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他, salary_video-工资视频, tax_video-个税视频, checkin_video-打卡视频, work_video-工作视频',
  312. example: AssetType.SALARY
  313. }),
  314. assetFileType: z.nativeEnum(AssetFileType).openapi({
  315. description: '资产文件类型:image-图片, video-视频',
  316. example: AssetFileType.IMAGE
  317. }),
  318. fileId: z.coerce.number().int().positive().openapi({
  319. description: '文件ID,引用files表',
  320. example: 1
  321. }),
  322. relatedTime: z.coerce.date().optional().openapi({
  323. description: '关联时间',
  324. example: '2024-01-01T00:00:00Z'
  325. })
  326. });
  327. // 分页查询参数Schema
  328. export const PaginationQuerySchema = z.object({
  329. skip: z.coerce.number().int().min(0).default(0).optional().openapi({
  330. description: '跳过记录数',
  331. example: 0
  332. }),
  333. take: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
  334. description: '获取记录数',
  335. example: 10
  336. })
  337. });
  338. // 删除订单DTO
  339. export const DeleteOrderSchema = z.object({
  340. id: z.number().int().positive().openapi({
  341. description: '订单ID',
  342. example: 1
  343. })
  344. });
  345. // 删除订单人员DTO
  346. export const DeleteOrderPersonSchema = z.object({
  347. id: z.number().int().positive().openapi({
  348. description: '关联ID',
  349. example: 1
  350. })
  351. });
  352. // 删除订单人员资产DTO
  353. export const DeleteOrderPersonAssetSchema = z.object({
  354. id: z.number().int().positive().openapi({
  355. description: '关联ID',
  356. example: 1
  357. })
  358. });
  359. // 类型定义
  360. export type EmploymentOrder = z.infer<typeof EmploymentOrderSchema>;
  361. export type CreateEmploymentOrderDto = z.infer<typeof CreateEmploymentOrderSchema>;
  362. export type UpdateEmploymentOrderDto = z.infer<typeof UpdateEmploymentOrderSchema>;
  363. export type OrderPerson = z.infer<typeof OrderPersonSchema>;
  364. export type CreateOrderPersonDto = z.infer<typeof CreateOrderPersonSchema>;
  365. export type OrderPersonAsset = z.infer<typeof OrderPersonAssetSchema>;
  366. export type CreateOrderPersonAssetDto = z.infer<typeof CreateOrderPersonAssetSchema>;
  367. export type PaginationQuery = z.infer<typeof PaginationQuerySchema>;
  368. export type DeleteOrderDto = z.infer<typeof DeleteOrderSchema>;
  369. export type DeleteOrderPersonDto = z.infer<typeof DeleteOrderPersonSchema>;
  370. export type DeleteOrderPersonAssetDto = z.infer<typeof DeleteOrderPersonAssetSchema>;
  371. export type UpdatePersonWorkStatusDto = z.infer<typeof UpdatePersonWorkStatusSchema>;
  372. // 查询订单参数Schema
  373. export const QueryOrderSchema = z.object({
  374. orderName: z.string().optional().openapi({
  375. description: '订单名称',
  376. example: '2024年Q1用工订单'
  377. }),
  378. platformId: z.coerce.number().int().positive().optional().openapi({
  379. description: '用人平台ID',
  380. example: 1
  381. }),
  382. companyId: z.coerce.number().int().positive().optional().openapi({
  383. description: '用人单位ID',
  384. example: 1
  385. }),
  386. channelId: z.coerce.number().int().positive().optional().openapi({
  387. description: '渠道ID',
  388. example: 1
  389. }),
  390. orderStatus: z.nativeEnum(OrderStatus).optional().openapi({
  391. description: '订单状态',
  392. example: OrderStatus.DRAFT
  393. }),
  394. startDate: z.string().optional().openapi({
  395. description: '开始日期(YYYY-MM-DD格式)',
  396. example: '2024-01-01'
  397. }),
  398. endDate: z.string().optional().openapi({
  399. description: '结束日期(YYYY-MM-DD格式)',
  400. example: '2024-12-31'
  401. }),
  402. page: z.coerce.number().int().min(1).default(1).optional().openapi({
  403. description: '页码',
  404. example: 1
  405. }),
  406. limit: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
  407. description: '每页数量',
  408. example: 10
  409. })
  410. });
  411. // 查询订单人员资产参数Schema
  412. export const QueryOrderPersonAssetSchema = z.object({
  413. orderId: z.coerce.number().int().positive().optional().openapi({
  414. description: '订单ID',
  415. example: 1
  416. }),
  417. personId: z.coerce.number().int().positive().optional().openapi({
  418. description: '人员ID',
  419. example: 1
  420. }),
  421. assetType: z.nativeEnum(AssetType).optional().openapi({
  422. description: '资产类型:tax-税务, salary-薪资, job_result-工作成果, contract_sign-合同签署, disability_cert-残疾证明, other-其他, salary_video-工资视频, tax_video-个税视频, checkin_video-打卡视频, work_video-工作视频',
  423. example: AssetType.SALARY
  424. }),
  425. assetFileType: z.nativeEnum(AssetFileType).optional().openapi({
  426. description: '资产文件类型',
  427. example: AssetFileType.IMAGE
  428. }),
  429. page: z.coerce.number().int().min(1).default(1).optional().openapi({
  430. description: '页码',
  431. example: 1
  432. }),
  433. limit: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
  434. description: '每页数量',
  435. example: 10
  436. })
  437. });
  438. // 为兼容性添加别名导出
  439. export const CreateOrderSchema = CreateEmploymentOrderSchema;
  440. export const UpdateOrderSchema = UpdateEmploymentOrderSchema;
  441. export const BatchAddPersonsSchema = z.object({
  442. persons: z.array(BatchAddPersonItemSchema).openapi({
  443. description: '人员列表',
  444. example: [{ personId: 1, joinDate: '2024-01-01T00:00:00Z', salaryDetail: 5000 }]
  445. })
  446. });
  447. // 更新订单人员工作状态DTO
  448. export const UpdatePersonWorkStatusSchema = z.object({
  449. orderId: z.coerce.number().int().positive().openapi({
  450. description: '订单ID',
  451. example: 1
  452. }),
  453. personId: z.coerce.number().int().positive().openapi({
  454. description: '人员ID',
  455. example: 1
  456. }),
  457. workStatus: z.nativeEnum(WorkStatus).openapi({
  458. description: '工作状态:not_working-未就业, pre_working-待就业, working-已就业, resigned-已离职',
  459. example: WorkStatus.WORKING
  460. })
  461. });
  462. // 打卡数据统计响应Schema
  463. export const CheckinStatisticsResponseSchema = z.object({
  464. companyId: z.number().int().positive().openapi({
  465. description: '企业ID',
  466. example: 1
  467. }),
  468. checkinVideoCount: z.number().int().min(0).openapi({
  469. description: '打卡视频数量',
  470. example: 10
  471. }),
  472. totalVideos: z.number().int().min(0).openapi({
  473. description: '总视频数量',
  474. example: 25
  475. })
  476. });
  477. // 视频统计项Schema
  478. export const VideoStatItemSchema = z.object({
  479. assetType: z.nativeEnum(AssetType).openapi({
  480. description: '视频类型',
  481. example: AssetType.CHECKIN_VIDEO
  482. }),
  483. count: z.number().int().min(0).openapi({
  484. description: '该类型视频数量',
  485. example: 5
  486. }),
  487. percentage: z.number().min(0).max(100).openapi({
  488. description: '占比百分比',
  489. example: 20.0
  490. })
  491. });
  492. // 视频分类统计响应Schema
  493. export const VideoStatisticsResponseSchema = z.object({
  494. companyId: z.number().int().positive().openapi({
  495. description: '企业ID',
  496. example: 1
  497. }),
  498. stats: z.array(VideoStatItemSchema).openapi({
  499. description: '视频分类统计列表'
  500. }),
  501. total: z.number().int().min(0).openapi({
  502. description: '视频总数',
  503. example: 25
  504. })
  505. });
  506. // 企业订单查询参数Schema
  507. export const CompanyOrdersQuerySchema = z.object({
  508. companyId: z.coerce.number().int().positive().optional().openapi({
  509. description: '企业ID(从认证用户获取,可覆盖)',
  510. example: 1
  511. }),
  512. orderName: z.string().optional().openapi({
  513. description: '订单名称过滤',
  514. example: '2024年Q1'
  515. }),
  516. orderStatus: z.nativeEnum(OrderStatus).optional().openapi({
  517. description: '订单状态过滤',
  518. example: OrderStatus.CONFIRMED
  519. }),
  520. startDate: z.string().optional().openapi({
  521. description: '开始日期(YYYY-MM-DD格式)',
  522. example: '2024-01-01'
  523. }),
  524. endDate: z.string().optional().openapi({
  525. description: '结束日期(YYYY-MM-DD格式)',
  526. example: '2024-12-31'
  527. }),
  528. page: z.coerce.number().int().min(1).default(1).optional().openapi({
  529. description: '页码',
  530. example: 1
  531. }),
  532. limit: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
  533. description: '每页数量',
  534. example: 10
  535. }),
  536. sortBy: z.enum(['createTime', 'updateTime', 'orderName']).default('createTime').optional().openapi({
  537. description: '排序字段',
  538. example: 'createTime'
  539. }),
  540. sortOrder: z.enum(['ASC', 'DESC']).default('DESC').optional().openapi({
  541. description: '排序方向',
  542. example: 'DESC'
  543. })
  544. });
  545. // 企业维度视频查询参数Schema
  546. export const CompanyVideosQuerySchema = z.object({
  547. companyId: z.coerce.number().int().positive().optional().openapi({
  548. description: '企业ID(从认证用户获取,可覆盖)',
  549. example: 1
  550. }),
  551. assetType: z.nativeEnum(AssetType).optional().openapi({
  552. description: '视频类型过滤',
  553. example: AssetType.CHECKIN_VIDEO
  554. }),
  555. page: z.coerce.number().int().min(1).default(1).optional().openapi({
  556. description: '页码',
  557. example: 1
  558. }),
  559. pageSize: z.coerce.number().int().min(1).max(100).default(10).optional().openapi({
  560. description: '每页数量',
  561. example: 10
  562. }),
  563. sortBy: z.enum(['relatedTime', 'createTime', 'updateTime']).default('relatedTime').optional().openapi({
  564. description: '排序字段:relatedTime-关联时间, createTime-创建时间, updateTime-更新时间',
  565. example: 'relatedTime'
  566. }),
  567. sortOrder: z.enum(['ASC', 'DESC']).default('DESC').optional().openapi({
  568. description: '排序方向',
  569. example: 'DESC'
  570. })
  571. });
  572. // 简化的文件schema,用于视频查询响应
  573. const SimpleFileSchema = z.object({
  574. id: z.number().int().positive().openapi({
  575. description: '文件ID',
  576. example: 1
  577. }),
  578. name: z.string().max(255).openapi({
  579. description: '文件名称',
  580. example: '打卡视频.mp4'
  581. }),
  582. type: z.string().max(50).nullable().openapi({
  583. description: '文件类型',
  584. example: 'video/mp4'
  585. }),
  586. size: z.number().int().positive().nullable().openapi({
  587. description: '文件大小,单位字节',
  588. example: 102400
  589. }),
  590. path: z.string().max(512).openapi({
  591. description: '文件存储路径',
  592. example: '/uploads/videos/2024/checkin-video.mp4'
  593. }),
  594. fullUrl: z.string().optional().openapi({
  595. description: '完整文件访问URL',
  596. example: 'https://minio.example.com/d8dai/uploads/videos/2024/checkin-video.mp4'
  597. }),
  598. description: z.string().nullable().openapi({
  599. description: '文件描述',
  600. example: '员工打卡视频记录'
  601. }),
  602. uploadTime: z.coerce.date().openapi({
  603. description: '上传时间',
  604. example: '2024-01-15T10:30:00Z'
  605. })
  606. });
  607. // 企业维度视频响应Schema
  608. export const CompanyVideoResponseSchema = z.object({
  609. id: z.number().int().positive().openapi({
  610. description: '资产ID',
  611. example: 1
  612. }),
  613. orderId: z.number().int().positive().openapi({
  614. description: '订单ID',
  615. example: 1
  616. }),
  617. personId: z.number().int().positive().openapi({
  618. description: '人员ID',
  619. example: 1
  620. }),
  621. assetType: z.nativeEnum(AssetType).openapi({
  622. description: '视频类型',
  623. example: AssetType.CHECKIN_VIDEO
  624. }),
  625. assetFileType: z.nativeEnum(AssetFileType).openapi({
  626. description: '资产文件类型',
  627. example: AssetFileType.VIDEO
  628. }),
  629. fileId: z.number().int().positive().openapi({
  630. description: '文件ID',
  631. example: 1
  632. }),
  633. file: SimpleFileSchema.optional().openapi({
  634. description: '文件详情'
  635. }),
  636. relatedTime: z.coerce.date().openapi({
  637. description: '关联时间',
  638. example: '2024-01-15T10:30:00Z'
  639. }),
  640. createTime: z.coerce.date().openapi({
  641. description: '创建时间',
  642. example: '2024-01-15T10:30:00Z'
  643. }),
  644. updateTime: z.coerce.date().openapi({
  645. description: '更新时间',
  646. example: '2024-01-15T10:30:00Z'
  647. })
  648. });
  649. // 企业维度视频列表响应Schema
  650. export const CompanyVideoListResponseSchema = z.object({
  651. data: z.array(CompanyVideoResponseSchema).openapi({
  652. description: '视频列表'
  653. }),
  654. total: z.number().int().openapi({
  655. description: '总记录数',
  656. example: 100
  657. })
  658. });
  659. // 批量下载范围枚举
  660. export enum DownloadScope {
  661. COMPANY = 'company',
  662. PERSON = 'person'
  663. }
  664. // 批量下载请求Schema
  665. export const BatchDownloadRequestSchema = z.object({
  666. downloadScope: z.nativeEnum(DownloadScope).openapi({
  667. description: '下载范围:company-企业维度, person-个人维度',
  668. example: DownloadScope.COMPANY
  669. }),
  670. companyId: z.coerce.number().int().positive().optional().openapi({
  671. description: '企业ID(下载范围为company时必需,从认证用户获取可覆盖)',
  672. example: 1
  673. }),
  674. personId: z.coerce.number().int().positive().optional().openapi({
  675. description: '人员ID(下载范围为person时必需)',
  676. example: 1
  677. }),
  678. assetTypes: z.array(z.nativeEnum(AssetType)).optional().openapi({
  679. description: '视频类型过滤数组',
  680. example: [AssetType.CHECKIN_VIDEO, AssetType.WORK_VIDEO]
  681. }),
  682. fileIds: z.array(z.coerce.number().int().positive()).optional().openapi({
  683. description: '文件ID列表(指定具体文件下载)',
  684. example: [1, 2, 3]
  685. })
  686. });
  687. // 批量下载文件项Schema
  688. export const BatchDownloadFileItemSchema = z.object({
  689. id: z.number().int().positive().openapi({
  690. description: '文件ID',
  691. example: 1
  692. }),
  693. name: z.string().openapi({
  694. description: '文件名称',
  695. example: '打卡视频.mp4'
  696. }),
  697. size: z.number().int().positive().nullable().openapi({
  698. description: '文件大小,单位字节',
  699. example: 102400
  700. }),
  701. url: z.string().openapi({
  702. description: '文件访问URL(预签名URL)',
  703. example: 'https://minio.example.com/d8dai/uploads/videos/2024/checkin-video.mp4?X-Amz-Algorithm=...'
  704. }),
  705. assetType: z.nativeEnum(AssetType).openapi({
  706. description: '视频类型',
  707. example: AssetType.CHECKIN_VIDEO
  708. }),
  709. orderId: z.number().int().positive().openapi({
  710. description: '订单ID',
  711. example: 1
  712. }),
  713. personId: z.number().int().positive().openapi({
  714. description: '人员ID',
  715. example: 1
  716. }),
  717. relatedTime: z.coerce.date().openapi({
  718. description: '关联时间',
  719. example: '2024-01-15T10:30:00Z'
  720. })
  721. });
  722. // 批量下载响应Schema
  723. export const BatchDownloadResponseSchema = z.object({
  724. success: z.boolean().openapi({
  725. description: '是否成功',
  726. example: true
  727. }),
  728. message: z.string().openapi({
  729. description: '操作结果消息',
  730. example: '批量下载成功,共生成3个文件URL'
  731. }),
  732. files: z.array(BatchDownloadFileItemSchema).openapi({
  733. description: '文件URL列表'
  734. }),
  735. totalFiles: z.number().int().openapi({
  736. description: '文件总数',
  737. example: 3
  738. })
  739. });
  740. // 更新视频审核状态请求Schema
  741. export const UpdateAssetStatusSchema = z.object({
  742. status: z.nativeEnum(AssetStatus).openapi({
  743. description: '视频审核状态:pending-待审核, verified-已验证, rejected-已拒绝',
  744. example: AssetStatus.VERIFIED
  745. })
  746. });
  747. // 类型定义
  748. export type CheckinStatisticsResponse = z.infer<typeof CheckinStatisticsResponseSchema>;
  749. export type VideoStatItem = z.infer<typeof VideoStatItemSchema>;
  750. export type VideoStatisticsResponse = z.infer<typeof VideoStatisticsResponseSchema>;
  751. export type CompanyOrdersQuery = z.infer<typeof CompanyOrdersQuerySchema>;
  752. export type CompanyVideosQuery = z.infer<typeof CompanyVideosQuerySchema>;
  753. export type CompanyVideoResponse = z.infer<typeof CompanyVideoResponseSchema>;
  754. export type CompanyVideoListResponse = z.infer<typeof CompanyVideoListResponseSchema>;
  755. export type BatchDownloadRequest = z.infer<typeof BatchDownloadRequestSchema>;
  756. export type BatchDownloadFileItem = z.infer<typeof BatchDownloadFileItemSchema>;
  757. export type BatchDownloadResponse = z.infer<typeof BatchDownloadResponseSchema>;
  758. export type UpdateAssetStatus = z.infer<typeof UpdateAssetStatusSchema>;
  759. export { OrderStatus, WorkStatus } from '@d8d/allin-enums';