|
@@ -0,0 +1,220 @@
|
|
|
|
|
+import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from 'typeorm';
|
|
|
|
|
+import { z } from 'zod';
|
|
|
|
|
+import { Department } from '@/server/modules/departments/department.entity';
|
|
|
|
|
+import { Role } from '@/server/modules/roles/role.entity';
|
|
|
|
|
+import { Lead } from '@/server/modules/leads/lead.entity';
|
|
|
|
|
+import { Opportunity } from '@/server/modules/opportunities/opportunity.entity';
|
|
|
|
|
+import { Contract } from '@/server/modules/contracts/contract.entity';
|
|
|
|
|
+import { Ticket } from '@/server/modules/tickets/ticket.entity';
|
|
|
|
|
+
|
|
|
|
|
+@Entity('users')
|
|
|
|
|
+export class User {
|
|
|
|
|
+ @PrimaryGeneratedColumn({ unsigned: true })
|
|
|
|
|
+ id!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'username',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 50,
|
|
|
|
|
+ nullable: false,
|
|
|
|
|
+ unique: true,
|
|
|
|
|
+ comment: '用户名'
|
|
|
|
|
+ })
|
|
|
|
|
+ username!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'password',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 100,
|
|
|
|
|
+ nullable: false,
|
|
|
|
|
+ comment: '密码(加密存储)'
|
|
|
|
|
+ })
|
|
|
|
|
+ password!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'name',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 50,
|
|
|
|
|
+ nullable: false,
|
|
|
|
|
+ comment: '姓名'
|
|
|
|
|
+ })
|
|
|
|
|
+ name!: string;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'email',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 100,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ unique: true,
|
|
|
|
|
+ comment: '邮箱'
|
|
|
|
|
+ })
|
|
|
|
|
+ email!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'phone',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 20,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '电话'
|
|
|
|
|
+ })
|
|
|
|
|
+ phone!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'department_id',
|
|
|
|
|
+ type: 'int',
|
|
|
|
|
+ unsigned: true,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '部门ID'
|
|
|
|
|
+ })
|
|
|
|
|
+ departmentId!: number | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'role_id',
|
|
|
|
|
+ type: 'int',
|
|
|
|
|
+ unsigned: true,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '角色ID'
|
|
|
|
|
+ })
|
|
|
|
|
+ roleId!: number | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'position',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 50,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '职位'
|
|
|
|
|
+ })
|
|
|
|
|
+ position!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'avatar_url',
|
|
|
|
|
+ type: 'varchar',
|
|
|
|
|
+ length: 255,
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '头像URL'
|
|
|
|
|
+ })
|
|
|
|
|
+ avatarUrl!: string | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'last_login_at',
|
|
|
|
|
+ type: 'timestamp',
|
|
|
|
|
+ nullable: true,
|
|
|
|
|
+ comment: '最后登录时间'
|
|
|
|
|
+ })
|
|
|
|
|
+ lastLoginAt!: Date | null;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'status',
|
|
|
|
|
+ type: 'tinyint',
|
|
|
|
|
+ default: 1,
|
|
|
|
|
+ comment: '状态(0-禁用, 1-启用)'
|
|
|
|
|
+ })
|
|
|
|
|
+ status!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'is_deleted',
|
|
|
|
|
+ type: 'tinyint',
|
|
|
|
|
+ default: 0,
|
|
|
|
|
+ comment: '删除状态(0-未删除, 1-已删除)'
|
|
|
|
|
+ })
|
|
|
|
|
+ isDeleted!: number;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'created_at',
|
|
|
|
|
+ type: 'timestamp',
|
|
|
|
|
+ default: () => 'CURRENT_TIMESTAMP',
|
|
|
|
|
+ comment: '创建时间'
|
|
|
|
|
+ })
|
|
|
|
|
+ createdAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ @Column({
|
|
|
|
|
+ name: 'updated_at',
|
|
|
|
|
+ type: 'timestamp',
|
|
|
|
|
+ default: () => 'CURRENT_TIMESTAMP',
|
|
|
|
|
+ onUpdate: 'CURRENT_TIMESTAMP',
|
|
|
|
|
+ comment: '更新时间'
|
|
|
|
|
+ })
|
|
|
|
|
+ updatedAt!: Date;
|
|
|
|
|
+
|
|
|
|
|
+ // 关系定义
|
|
|
|
|
+ @ManyToOne(() => Department, department => department.id)
|
|
|
|
|
+ department!: Department;
|
|
|
|
|
+
|
|
|
|
|
+ @ManyToOne(() => Role, role => role.id)
|
|
|
|
|
+ role!: Role;
|
|
|
|
|
+
|
|
|
|
|
+ @OneToMany(() => Lead, lead => lead.assignedUser)
|
|
|
|
|
+ assignedLeads!: Lead[];
|
|
|
|
|
+
|
|
|
|
|
+ @OneToMany(() => Opportunity, opportunity => opportunity.assignedUser)
|
|
|
|
|
+ assignedOpportunities!: Opportunity[];
|
|
|
|
|
+
|
|
|
|
|
+ @OneToMany(() => Contract, contract => contract.signedUser)
|
|
|
|
|
+ signedContracts!: Contract[];
|
|
|
|
|
+
|
|
|
|
|
+ @OneToMany(() => Ticket, ticket => ticket.assignedUser)
|
|
|
|
|
+ assignedTickets!: Ticket[];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export const UserSchema = z.object({
|
|
|
|
|
+ id: z.number().int().positive().openapi({
|
|
|
|
|
+ description: '用户ID',
|
|
|
|
|
+ example: 1
|
|
|
|
|
+ }),
|
|
|
|
|
+ username: z.string().min(1).max(50).openapi({
|
|
|
|
|
+ description: '用户名',
|
|
|
|
|
+ example: 'admin'
|
|
|
|
|
+ }),
|
|
|
|
|
+ password: z.string().min(1).max(100).openapi({
|
|
|
|
|
+ description: '密码(加密存储)',
|
|
|
|
|
+ example: '$2a$10$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
|
|
|
|
|
+ }),
|
|
|
|
|
+ name: z.string().min(1).max(50).openapi({
|
|
|
|
|
+ description: '姓名',
|
|
|
|
|
+ example: '管理员'
|
|
|
|
|
+ }),
|
|
|
|
|
+ email: z.string().email().max(100).nullable().openapi({
|
|
|
|
|
+ description: '邮箱',
|
|
|
|
|
+ example: 'admin@example.com'
|
|
|
|
|
+ }),
|
|
|
|
|
+ phone: z.string().max(20).nullable().openapi({
|
|
|
|
|
+ description: '电话',
|
|
|
|
|
+ example: '13800138000'
|
|
|
|
|
+ }),
|
|
|
|
|
+ departmentId: z.number().int().positive().nullable().openapi({
|
|
|
|
|
+ description: '部门ID',
|
|
|
|
|
+ example: 1
|
|
|
|
|
+ }),
|
|
|
|
|
+ roleId: z.number().int().positive().nullable().openapi({
|
|
|
|
|
+ description: '角色ID',
|
|
|
|
|
+ example: 1
|
|
|
|
|
+ }),
|
|
|
|
|
+ position: z.string().max(50).nullable().openapi({
|
|
|
|
|
+ description: '职位',
|
|
|
|
|
+ example: '系统管理员'
|
|
|
|
|
+ }),
|
|
|
|
|
+ avatarUrl: z.string().url().max(255).nullable().openapi({
|
|
|
|
|
+ description: '头像URL',
|
|
|
|
|
+ example: 'https://example.com/avatars/admin.png'
|
|
|
|
|
+ }),
|
|
|
|
|
+ lastLoginAt: z.date().nullable().openapi({
|
|
|
|
|
+ description: '最后登录时间',
|
|
|
|
|
+ example: '2023-01-10T08:30:00Z'
|
|
|
|
|
+ }),
|
|
|
|
|
+ status: z.number().int().min(0).max(1).openapi({
|
|
|
|
|
+ description: '状态(0-禁用, 1-启用)',
|
|
|
|
|
+ example: 1
|
|
|
|
|
+ }),
|
|
|
|
|
+ isDeleted: z.number().int().min(0).max(1).openapi({
|
|
|
|
|
+ description: '删除状态(0-未删除, 1-已删除)',
|
|
|
|
|
+ example: 0
|
|
|
|
|
+ }),
|
|
|
|
|
+ createdAt: z.date().openapi({
|
|
|
|
|
+ description: '创建时间',
|
|
|
|
|
+ example: '2023-01-01T00:00:00Z'
|
|
|
|
|
+ }),
|
|
|
|
|
+ updatedAt: z.date().openapi({
|
|
|
|
|
+ description: '更新时间',
|
|
|
|
|
+ example: '2023-01-01T00:00:00Z'
|
|
|
|
|
+ })
|
|
|
|
|
+});
|