order.schema.ts 26 KB

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