feat: 添加Redis支持、雪花ID生成器及集群模式

- 新增Redis插件,支持分布式锁及缓存功能
- 引入雪花ID生成器,用于生成唯一用户ID
- 实现集群模式,支持多线程运行
- 优化用户注册逻辑,增加分布式锁机制
- 更新配置文件,添加Redis及雪花ID相关配置
- 修复部分代码格式及数据库字段类型
This commit is contained in:
expressgy 2025-03-26 00:42:33 +08:00
parent 3d4af91569
commit fdc4d67092
34 changed files with 2820 additions and 2260 deletions

14
.env
View File

@ -1,5 +1,5 @@
# 服务器配置
PORT=9000
PORT=19000
HOST=0.0.0.0
BACKLOG=511
@ -17,3 +17,15 @@ DB_USER=nie
DB_PASSWORD=Hxl1314521
DB_NAME=yuheng
DB_PORT=3306
# redis
REDIS_HOST=172.16.1.10
REDIS_PORT=16379
REDIS_PASSWORD=Hxl1314521
REDIS_DB=9
# 数据中心ID
DATACENTER_ID=1
# 机器ID
MACHINE_ID=1

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,13 @@
{
"version": "7",
"dialect": "mysql",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1742546365192,
"tag": "0000_skinny_bloodaxe",
"breakpoints": true
}
]
}
"version": "7",
"dialect": "mysql",
"entries": [
{
"idx": 0,
"version": "5",
"when": 1742546365192,
"tag": "0000_skinny_bloodaxe",
"breakpoints": true
}
]
}

View File

@ -1,3 +1,2 @@
import { relations } from "drizzle-orm/relations";
import { } from "./schema";
import { relations } from 'drizzle-orm/relations';
import {} from './schema';

View File

@ -1,242 +1,339 @@
import { mysqlTable, mysqlSchema, AnyMySqlColumn, primaryKey, unique, bigint, int, tinyint, varchar, datetime } from "drizzle-orm/mysql-core"
import { sql } from "drizzle-orm"
import {
mysqlTable,
mysqlSchema,
AnyMySqlColumn,
primaryKey,
unique,
bigint,
int,
tinyint,
varchar,
datetime,
} from 'drizzle-orm/mysql-core';
import { sql } from 'drizzle-orm';
export const sysDict = mysqlTable("sys_dict", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
pid: bigint({ mode: "number" }).notNull(),
module: tinyint(),
dictKey: varchar("dict_key", { length: 255 }),
value: varchar({ length: 255 }),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_dict_id"}),
unique("uniq_dict_key").on(table.dictKey, table.pid),
]);
export const sysDict = mysqlTable(
'sys_dict',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
version: int().default(0).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
module: tinyint(),
dictKey: varchar('dict_key', { length: 255 }),
value: varchar({ length: 255 }),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_dict_id' }),
unique('uniq_dict_key').on(table.dictKey, table.pid),
],
);
export const sysOrganization = mysqlTable("sys_organization", {
orgId: bigint("org_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
orgName: varchar("org_name", { length: 255 }),
orgCode: varchar("org_code", { length: 128 }),
orgType: int("org_type").notNull(),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.orgId], name: "sys_organization_org_id"}),
unique("uniq_org_code").on(table.orgCode, table.pid),
unique("uniq_org_name").on(table.orgName, table.pid),
]);
export const sysOrganization = mysqlTable(
'sys_organization',
{
orgId: bigint('org_id', { mode: 'number' }).autoincrement().notNull(),
pid: bigint({ mode: 'number' }).notNull(),
orgName: varchar('org_name', { length: 255 }),
orgCode: varchar('org_code', { length: 128 }),
orgType: int('org_type').notNull(),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.orgId], name: 'sys_organization_org_id' }),
unique('uniq_org_code').on(table.orgCode, table.pid),
unique('uniq_org_name').on(table.orgName, table.pid),
],
);
export const sysOrganizationManager = mysqlTable("sys_organization_manager", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
orgId: bigint("org_id", { mode: "number" }).notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
rank: int().notNull(),
description: varchar({ length: 255 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_organization_manager_id"}),
unique("uniq_org_user").on(table.orgId, table.userId),
]);
export const sysOrganizationManager = mysqlTable(
'sys_organization_manager',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
version: int().default(0).notNull(),
orgId: bigint('org_id', { mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
rank: int().notNull(),
description: varchar({ length: 255 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_organization_manager_id' }),
unique('uniq_org_user').on(table.orgId, table.userId),
],
);
export const sysPermission = mysqlTable("sys_permission", {
permId: bigint("perm_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
permName: varchar("perm_name", { length: 255 }).notNull(),
permKey: varchar("perm_key", { length: 255 }).notNull(),
url: varchar({ length: 255 }),
avatarUrl: varchar("avatar_url", { length: 255 }),
description: varchar({ length: 255 }),
permType: int("perm_type").notNull(),
isVisible: int("is_visible").default(0).notNull(),
version: int().default(0).notNull(),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.permId], name: "sys_permission_perm_id"}),
unique("uniq_pid_name").on(table.permName, table.pid),
unique("uniq_perm_key").on(table.permKey),
]);
export const sysPermission = mysqlTable(
'sys_permission',
{
permId: bigint('perm_id', { mode: 'number' }).autoincrement().notNull(),
pid: bigint({ mode: 'number' }).notNull(),
permName: varchar('perm_name', { length: 255 }).notNull(),
permKey: varchar('perm_key', { length: 255 }).notNull(),
url: varchar({ length: 255 }),
avatarUrl: varchar('avatar_url', { length: 255 }),
description: varchar({ length: 255 }),
permType: int('perm_type').notNull(),
isVisible: int('is_visible').default(0).notNull(),
version: int().default(0).notNull(),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.permId], name: 'sys_permission_perm_id' }),
unique('uniq_pid_name').on(table.permName, table.pid),
unique('uniq_perm_key').on(table.permKey),
],
);
export const sysReRolePermission = mysqlTable("sys_re_role_permission", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
roleId: bigint("role_id", { mode: "number" }).notNull(),
permId: bigint("perm_id", { mode: "number" }).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_role_permission_id"}),
unique("uniq_perm_role").on(table.roleId, table.permId),
]);
export const sysReRolePermission = mysqlTable(
'sys_re_role_permission',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
roleId: bigint('role_id', { mode: 'number' }).notNull(),
permId: bigint('perm_id', { mode: 'number' }).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_role_permission_id' }),
unique('uniq_perm_role').on(table.roleId, table.permId),
],
);
export const sysReUserOrganization = mysqlTable("sys_re_user_organization", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
orgId: bigint("org_id", { mode: "number" }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_user_organization_id"}),
unique("uniq_user_org").on(table.userId, table.orgId),
]);
export const sysReUserOrganization = mysqlTable(
'sys_re_user_organization',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
orgId: bigint('org_id', { mode: 'number' }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_user_organization_id' }),
unique('uniq_user_org').on(table.userId, table.orgId),
],
);
export const sysReUserRole = mysqlTable("sys_re_user_role", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
roleId: bigint("role_id", { mode: "number" }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_user_role_id"}),
unique("uniq_user_role").on(table.userId, table.roleId),
]);
export const sysReUserRole = mysqlTable(
'sys_re_user_role',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
roleId: bigint('role_id', { mode: 'number' }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_user_role_id' }),
unique('uniq_user_role').on(table.userId, table.roleId),
],
);
export const sysRole = mysqlTable("sys_role", {
roleId: bigint("role_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
roleName: varchar("role_name", { length: 255 }).notNull(),
roleKey: varchar("role_key", { length: 255 }).notNull(),
description: varchar({ length: 255 }),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.roleId], name: "sys_role_role_id"}),
unique("uniq_role_pid").on(table.roleName, table.pid),
]);
export const sysRole = mysqlTable(
'sys_role',
{
roleId: bigint('role_id', { mode: 'number' }).autoincrement().notNull(),
pid: bigint({ mode: 'number' }).notNull(),
roleName: varchar('role_name', { length: 255 }).notNull(),
roleKey: varchar('role_key', { length: 255 }).notNull(),
description: varchar({ length: 255 }),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.roleId], name: 'sys_role_role_id' }),
unique('uniq_role_pid').on(table.roleName, table.pid),
],
);
export const sysUser = mysqlTable("sys_user", {
userId: bigint("user_id", { mode: "number" }).notNull(),
pid: bigint({ mode: "number" }).notNull(),
username: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull(),
phone: varchar({ length: 255 }),
avatarUrl: varchar("avatar_url", { length: 255 }),
userType: tinyint("user_type"),
status: tinyint().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.userId], name: "sys_user_user_id"}),
unique("uniq_username").on(table.username),
unique("uniq_email").on(table.email),
]);
export const sysUser = mysqlTable(
'sys_user',
{
userId: bigint('user_id', { mode: 'number' }).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
username: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull(),
phone: varchar({ length: 255 }),
avatarUrl: varchar('avatar_url', { length: 255 }),
userType: tinyint('user_type'),
status: tinyint().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.userId], name: 'sys_user_user_id' }),
unique('uniq_username').on(table.username),
unique('uniq_email').on(table.email),
],
);
export const sysUserAuth = mysqlTable("sys_user_auth", {
userId: bigint("user_id", { mode: "number" }).notNull(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
passwordModified: datetime("password_modified", { mode: 'string'}).notNull(),
passwordExpire: datetime("password_expire", { mode: 'string'}),
},
(table) => [
primaryKey({ columns: [table.userId], name: "sys_user_auth_user_id"}),
]);
export const sysUserAuth = mysqlTable(
'sys_user_auth',
{
userId: bigint('user_id', { mode: 'number' }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
passwordModified: datetime('password_modified', { mode: 'string' }).notNull(),
passwordExpire: datetime('password_expire', { mode: 'string' }),
},
table => [primaryKey({ columns: [table.userId], name: 'sys_user_auth_user_id' })],
);
export const sysUserAuthHistory = mysqlTable("sys_user_auth_history", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
modifiedAt: varchar("modified_at", { length: 255 }).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_auth_history_id"}),
]);
export const sysUserAuthHistory = mysqlTable(
'sys_user_auth_history',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
modifiedAt: varchar('modified_at', { length: 255 }).notNull(),
},
table => [primaryKey({ columns: [table.id], name: 'sys_user_auth_history_id' })],
);
export const sysUserFieldDefinition = mysqlTable("sys_user_field_definition", {
fieldId: bigint("field_id", { mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
fieldName: varchar("field_name", { length: 255 }).notNull(),
fieldKey: varchar("field_key", { length: 255 }).notNull(),
fieldType: tinyint("field_type").notNull(),
dictModule: int("dict_module"),
isRequired: tinyint("is_required").default(0).notNull(),
limit: int(),
description: varchar({ length: 255 }),
defaultValue: varchar("default_value", { length: 255 }),
defaultOptions: varchar("default_options", { length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.fieldId], name: "sys_user_field_definition_field_id"}),
unique("uniq_field_name").on(table.fieldName),
unique("uniq_field_key").on(table.fieldKey),
]);
export const sysUserFieldDefinition = mysqlTable(
'sys_user_field_definition',
{
fieldId: bigint('field_id', { mode: 'number' }).autoincrement().notNull(),
version: int().default(0).notNull(),
fieldName: varchar('field_name', { length: 255 }).notNull(),
fieldKey: varchar('field_key', { length: 255 }).notNull(),
fieldType: tinyint('field_type').notNull(),
dictModule: int('dict_module'),
isRequired: tinyint('is_required').default(0).notNull(),
limit: int(),
description: varchar({ length: 255 }),
defaultValue: varchar('default_value', { length: 255 }),
defaultOptions: varchar('default_options', { length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.fieldId], name: 'sys_user_field_definition_field_id' }),
unique('uniq_field_name').on(table.fieldName),
unique('uniq_field_key').on(table.fieldKey),
],
);
export const sysUserFieldValue = mysqlTable("sys_user_field_value", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
fieldId: int("field_id").notNull(),
value: varchar({ length: 4096 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_field_value_id"}),
unique("uniq_user_field").on(table.userId, table.fieldId),
]);
export const sysUserFieldValue = mysqlTable(
'sys_user_field_value',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
fieldId: int('field_id').notNull(),
value: varchar({ length: 4096 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_user_field_value_id' }),
unique('uniq_user_field').on(table.userId, table.fieldId),
],
);
export const sysUserProfile = mysqlTable("sys_user_profile", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
name: varchar({ length: 32 }).notNull(),
profileKey: varchar("profile_key", { length: 255 }).notNull(),
description: varchar({ length: 255 }),
content: varchar({ length: 255 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_profile_id"}),
unique("uniq_name").on(table.name),
unique("uniq_profile_key").on(table.profileKey),
]);
export const sysUserProfile = mysqlTable(
'sys_user_profile',
{
id: bigint({ mode: 'number' }).autoincrement().notNull(),
version: int().default(0).notNull(),
name: varchar({ length: 32 }).notNull(),
profileKey: varchar('profile_key', { length: 255 }).notNull(),
description: varchar({ length: 255 }),
content: varchar({ length: 255 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_user_profile_id' }),
unique('uniq_name').on(table.name),
unique('uniq_profile_key').on(table.profileKey),
],
);

View File

@ -4,7 +4,7 @@ import path from 'path';
// 加载环境变量
config({ path: path.resolve(process.cwd(), '.env') });
console.log('[NODE_ENV]:', process.env.NODE_ENV);
// console.log('[NODE_ENV]:', process.env.NODE_ENV);
// 基础配置
const baseConfig = {
@ -13,6 +13,11 @@ const baseConfig = {
serviceName: 'YuHeng',
description: 'User System',
version: '1.0.0',
deviceInfo: {
dataCenterId: process.env.DATACENTER_ID,
machineId: process.env.MACHINE_ID,
pid: process.pid,
},
server: {
port: process.env.PORT || 9000,
host: process.env.HOST || 'localhost',
@ -21,7 +26,7 @@ const baseConfig = {
profile: {
swagger: {
basePath: '/swagger',
}
},
},
logger: {
level: process.env.LOG_LEVEL || 'trace',
@ -38,11 +43,22 @@ const baseConfig = {
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'yuheng',
ssl: process.env.NODE_ENV === 'production' ? {
rejectUnauthorized: false,
servername: '' // 明确置空servername参数
} : null,
ssl:
process.env.NODE_ENV === 'production'
? {
rejectUnauthorized: false,
servername: '', // 明确置空servername参数
}
: null,
},
redis: {
host: process.env.REDIS_HOST || '127.0.0.1',
port: process.env.REDIS_PORT || 6379,
connectName: 'yuheng',
database: process.env.REDIS_DB || 9,
username: 'default',
password: process.env.REDIS_PASSWORD || 'Hxl1314521',
}
};
// 环境特定配置
@ -72,5 +88,6 @@ function deepMerge(target, source) {
}
return Object.assign({}, target, source);
}
const configInfo = deepMerge(baseConfig, envConfig[process.env.NODE_ENV] || envConfig.development);
export default deepMerge(baseConfig, envConfig[process.env.NODE_ENV] || envConfig.development);
export default configInfo;

97
package-lock.json generated
View File

@ -22,7 +22,8 @@
"mysql2": "^3.13.0",
"pino": "^9.6.0",
"pino-multi-stream": "^6.0.0",
"pino-pretty": "^13.0.0"
"pino-pretty": "^13.0.0",
"redis": "^4.7.0"
},
"devDependencies": {
"cross-env": "^7.0.3",
@ -1307,6 +1308,65 @@
"integrity": "sha512-VgffxawQde93xKxT3qap3OH+meZf7VaSB5Sqd4Rqc+FP5alWbpOyan/7tRbOAvynjpG3GpdtAuGU/NdhQpmrog==",
"license": "MIT"
},
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@redis/bloom/-/bloom-1.2.0.tgz",
"integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/client": {
"version": "1.6.0",
"resolved": "https://registry.npmmirror.com/@redis/client/-/client-1.6.0.tgz",
"integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.2",
"generic-pool": "3.9.0",
"yallist": "4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/@redis/graph": {
"version": "1.1.1",
"resolved": "https://registry.npmmirror.com/@redis/graph/-/graph-1.1.1.tgz",
"integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/json": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/@redis/json/-/json-1.0.7.tgz",
"integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/search": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/@redis/search/-/search-1.2.0.tgz",
"integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@redis/time-series": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@redis/time-series/-/time-series-1.1.0.tgz",
"integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==",
"license": "MIT",
"peerDependencies": {
"@redis/client": "^1.0.0"
}
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/@rtsao/scc/-/scc-1.1.0.tgz",
@ -1862,6 +1922,15 @@
"node": ">=10"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
@ -3368,6 +3437,15 @@
"is-property": "^1.0.2"
}
},
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmmirror.com/generic-pool/-/generic-pool-3.9.0.tgz",
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
"license": "MIT",
"engines": {
"node": ">= 4"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@ -5241,6 +5319,23 @@
"node": ">= 12.13.0"
}
},
"node_modules/redis": {
"version": "4.7.0",
"resolved": "https://registry.npmmirror.com/redis/-/redis-4.7.0.tgz",
"integrity": "sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==",
"license": "MIT",
"workspaces": [
"./packages/*"
],
"dependencies": {
"@redis/bloom": "1.2.0",
"@redis/client": "1.6.0",
"@redis/graph": "1.1.1",
"@redis/json": "1.0.7",
"@redis/search": "1.2.0",
"@redis/time-series": "1.1.0"
}
},
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmmirror.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",

View File

@ -21,8 +21,8 @@
"node": ">=22"
},
"scripts": {
"start": "cross-env NODE_ENV=production node src/application.js",
"dev": "cross-env NODE_ENV=development nodemon src/application.js",
"start": "cross-env NODE_ENV=production node src/cluster.js",
"dev": "cross-env NODE_ENV=development nodemon src/cluster.js",
"lint": "eslint src --ext .js",
"format": "prettier --write . --config ./.prettier.config.cjs --ignore-path .prettierignore",
"lint:format": "prettier --check . --config ./.prettier.config.cjs --ignore-path .prettierignore",
@ -48,7 +48,8 @@
"mysql2": "^3.13.0",
"pino": "^9.6.0",
"pino-multi-stream": "^6.0.0",
"pino-pretty": "^13.0.0"
"pino-pretty": "^13.0.0",
"redis": "^4.7.0"
},
"devDependencies": {
"cross-env": "^7.0.3",

View File

@ -1,17 +1,17 @@
// ESM
import '#start';
import Fastify from 'fastify';
import config from '#config/index';
import plugin from '#src/plugins/index';
import routes from '#src/routes/index';
import logger from '#src/utils/logger';
import loggerConfig from '#src/utils/logger';
import { ajvConfig } from '#utils/ajv/index';
async function start() {
export default async function Application(machineId = 1) {
config.deviceInfo.machineId = machineId;
// 创建Fastify实例
const fastify = new Fastify({
logger,
ajv: ajvConfig
logger: loggerConfig(config),
ajv: ajvConfig,
});
// 加载插件
await fastify.register(plugin, { config });
@ -20,4 +20,3 @@ async function start() {
// 启动服务器
await fastify.listen(config.server); // 使用配置中的服务器参数
}
start();

63
src/cluster.js Normal file
View File

@ -0,0 +1,63 @@
import echo from '#start';
import cluster from 'cluster';
import os from 'os';
import Application from './application.js';
// 获取 CPU 核心数
const numCPUs = os.cpus().length;
function sleep(time = 1000) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
}
// 启动集群
async function clusterStart() {
// 检查是否为主进程
if (cluster.isPrimary) {
echo();
// 设置负载均衡策略
cluster.schedulingPolicy = cluster.SCHED_RR;
console.log(`Primary ${process.pid} is running`);
if (process.env.NODE_ENV === 'production') {
console.log('>>>>>>>>>>>>>>多线程模式');
// 为每个 CPU 核心创建一个工作进程
for (let i = 0; i < /*numCPUs*/ 2; i++) {
const worker = cluster.fork();
worker.on('message', async message => {
if (message.type === 'workerStarted') {
worker.send({ type: 'workerId', id: i + 1 });
}
});
await sleep(100);
}
// 监听工作进程退出事件
cluster.on('exit', (worker, code, signal) => {
console.log(`Worker ${worker.process.pid} died`);
});
} else {
console.log('==============单线程模式');
Application();
}
} else {
// 工作进程
// 告诉主进程工作进程已启动
process.send({ type: 'workerStarted', pid: process.pid });
// 监听主进程发送的消息
process.on('message', message => {
if (message.type === 'workerId') {
const workerId = message.id;
console.log(
`Worker (ID ${workerId.toString().padStart(2, 0)}) ${process.pid.toString().padEnd(8, ' ')} started`,
);
// 启动服务器
Application(workerId);
}
});
}
}
clusterStart();

View File

@ -0,0 +1,27 @@
// | ------------------------------------------------------------
// | @版本: version 0.1
// | @创建人: 【Nie-x7129】
// | @E-mail: x71291@outlook.com
// | @所在项目: pac-auth
// | @文件描述: customType.ts -
// | @创建时间: 2024-06-04 16:27
// | @更新时间: 2024-06-04 16:27
// | @修改记录:
// | -*-*-*- (时间--修改人--修改说明) -*-*-*-
// | =
// | ------------------------------------------------------------
// 定义自定义类型
import { customType } from 'drizzle-orm/mysql-core';
// 写入读取是将bigint转化为string
export const bigintString = customType({
dataType() {
return 'bigint';
},
fromDriver(value) { // 数据库 -> JS
return value?.toString(); // 处理 null 值
},
toDriver(value) { // JS -> 数据库
return BigInt(value); // 确保写入时为数字类型
}
});

View File

@ -1,242 +1,341 @@
import { mysqlTable, mysqlSchema, primaryKey, unique, bigint, int, tinyint, varchar, datetime } from "drizzle-orm/mysql-core"
import { sql } from "drizzle-orm"
import {
mysqlTable,
mysqlSchema,
primaryKey,
unique,
int,
tinyint,
varchar,
datetime,
} from 'drizzle-orm/mysql-core';
import { sql } from 'drizzle-orm';
export const sysDict = mysqlTable("sys_dict", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
pid: bigint({ mode: "number" }).notNull(),
module: tinyint(),
dictKey: varchar("dict_key", { length: 255 }),
value: varchar({ length: 255 }),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_dict_id"}),
unique("uniq_dict_key").on(table.dictKey, table.pid),
]);
export const sysOrganization = mysqlTable("sys_organization", {
orgId: bigint("org_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
orgName: varchar("org_name", { length: 255 }),
orgCode: varchar("org_code", { length: 128 }),
orgType: int("org_type").notNull(),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.orgId], name: "sys_organization_org_id"}),
unique("uniq_org_code").on(table.orgCode, table.pid),
unique("uniq_org_name").on(table.orgName, table.pid),
]);
import { bigintString } from './customType.js';
const bigint = bigintString;
export const sysOrganizationManager = mysqlTable("sys_organization_manager", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
orgId: bigint("org_id", { mode: "number" }).notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
rank: int().notNull(),
description: varchar({ length: 255 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_organization_manager_id"}),
unique("uniq_org_user").on(table.orgId, table.userId),
]);
export const sysDict = mysqlTable(
'sys_dict',
{
id: bigint({ mode: 'number' }).notNull(),
version: int().default(0).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
module: tinyint(),
dictKey: varchar('dict_key', { length: 255 }),
value: varchar({ length: 255 }),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_dict_id' }),
unique('uniq_dict_key').on(table.dictKey, table.pid),
],
);
export const sysPermission = mysqlTable("sys_permission", {
permId: bigint("perm_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
permName: varchar("perm_name", { length: 255 }).notNull(),
permKey: varchar("perm_key", { length: 255 }).notNull(),
url: varchar({ length: 255 }),
avatarUrl: varchar("avatar_url", { length: 255 }),
description: varchar({ length: 255 }),
permType: int("perm_type").notNull(),
isVisible: int("is_visible").default(0).notNull(),
version: int().default(0).notNull(),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.permId], name: "sys_permission_perm_id"}),
unique("uniq_pid_name").on(table.permName, table.pid),
unique("uniq_perm_key").on(table.permKey),
]);
export const sysOrganization = mysqlTable(
'sys_organization',
{
orgId: bigint('org_id', { mode: 'number' }).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
orgName: varchar('org_name', { length: 255 }),
orgCode: varchar('org_code', { length: 128 }),
orgType: int('org_type').notNull(),
description: varchar({ length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.orgId], name: 'sys_organization_org_id' }),
unique('uniq_org_code').on(table.orgCode, table.pid),
unique('uniq_org_name').on(table.orgName, table.pid),
],
);
export const sysReRolePermission = mysqlTable("sys_re_role_permission", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
roleId: bigint("role_id", { mode: "number" }).notNull(),
permId: bigint("perm_id", { mode: "number" }).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_role_permission_id"}),
unique("uniq_perm_role").on(table.roleId, table.permId),
]);
export const sysOrganizationManager = mysqlTable(
'sys_organization_manager',
{
id: bigint({ mode: 'number' }).notNull(),
version: int().default(0).notNull(),
orgId: bigint('org_id', { mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
rank: int().notNull(),
description: varchar({ length: 255 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_organization_manager_id' }),
unique('uniq_org_user').on(table.orgId, table.userId),
],
);
export const sysReUserOrganization = mysqlTable("sys_re_user_organization", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
orgId: bigint("org_id", { mode: "number" }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_user_organization_id"}),
unique("uniq_user_org").on(table.userId, table.orgId),
]);
export const sysPermission = mysqlTable(
'sys_permission',
{
permId: bigint('perm_id', { mode: 'number' }).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
permName: varchar('perm_name', { length: 255 }).notNull(),
permKey: varchar('perm_key', { length: 255 }).notNull(),
url: varchar({ length: 255 }),
avatarUrl: varchar('avatar_url', { length: 255 }),
description: varchar({ length: 255 }),
permType: int('perm_type').notNull(),
isVisible: int('is_visible').default(0).notNull(),
version: int().default(0).notNull(),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.permId], name: 'sys_permission_perm_id' }),
unique('uniq_pid_name').on(table.permName, table.pid),
unique('uniq_perm_key').on(table.permKey),
],
);
export const sysReUserRole = mysqlTable("sys_re_user_role", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
roleId: bigint("role_id", { mode: "number" }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_re_user_role_id"}),
unique("uniq_user_role").on(table.userId, table.roleId),
]);
export const sysReRolePermission = mysqlTable(
'sys_re_role_permission',
{
id: bigint({ mode: 'number' }).notNull(),
roleId: bigint('role_id', { mode: 'number' }).notNull(),
permId: bigint('perm_id', { mode: 'number' }).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_role_permission_id' }),
unique('uniq_perm_role').on(table.roleId, table.permId),
],
);
export const sysRole = mysqlTable("sys_role", {
roleId: bigint("role_id", { mode: "number" }).autoincrement().notNull(),
pid: bigint({ mode: "number" }).notNull(),
roleName: varchar("role_name", { length: 255 }).notNull(),
roleKey: varchar("role_key", { length: 255 }).notNull(),
description: varchar({ length: 255 }),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.roleId], name: "sys_role_role_id"}),
unique("uniq_role_pid").on(table.roleName, table.pid),
]);
export const sysReUserOrganization = mysqlTable(
'sys_re_user_organization',
{
id: bigint({ mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
orgId: bigint('org_id', { mode: 'number' }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_user_organization_id' }),
unique('uniq_user_org').on(table.userId, table.orgId),
],
);
export const sysUser = mysqlTable("sys_user", {
userId: bigint("user_id", { mode: "number" }).notNull(),
pid: bigint({ mode: "number" }).notNull(),
username: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull(),
phone: varchar({ length: 255 }),
avatarUrl: varchar("avatar_url", { length: 255 }),
userType: tinyint("user_type"),
status: tinyint().default(0).notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.userId], name: "sys_user_user_id"}),
unique("uniq_username").on(table.username),
unique("uniq_email").on(table.email),
]);
export const sysReUserRole = mysqlTable(
'sys_re_user_role',
{
id: bigint({ mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
roleId: bigint('role_id', { mode: 'number' }).notNull(),
version: int().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_re_user_role_id' }),
unique('uniq_user_role').on(table.userId, table.roleId),
],
);
export const sysUserAuth = mysqlTable("sys_user_auth", {
userId: bigint("user_id", { mode: "number" }).notNull(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
passwordModified: datetime("password_modified", { mode: 'string'}).notNull(),
passwordExpire: datetime("password_expire", { mode: 'string'}),
},
(table) => [
primaryKey({ columns: [table.userId], name: "sys_user_auth_user_id"}),
]);
export const sysRole = mysqlTable(
'sys_role',
{
roleId: bigint('role_id', { mode: 'number' }).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
roleName: varchar('role_name', { length: 255 }).notNull(),
roleKey: varchar('role_key', { length: 255 }).notNull(),
description: varchar({ length: 255 }),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.roleId], name: 'sys_role_role_id' }),
unique('uniq_role_pid').on(table.roleName, table.pid),
],
);
export const sysUserAuthHistory = mysqlTable("sys_user_auth_history", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
passwordHash: varchar("password_hash", { length: 255 }).notNull(),
modifiedAt: varchar("modified_at", { length: 255 }).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_auth_history_id"}),
]);
export const sysUser = mysqlTable(
'sys_user',
{
userId: bigint('user_id', { mode: 'number' }).notNull(),
pid: bigint({ mode: 'number' }).notNull(),
username: varchar({ length: 255 }).notNull(),
email: varchar({ length: 255 }).notNull(),
phone: varchar({ length: 255 }),
avatarUrl: varchar('avatar_url', { length: 255 }),
userType: tinyint('user_type'),
status: tinyint().default(0).notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.userId], name: 'sys_user_user_id' }),
unique('uniq_username').on(table.username),
unique('uniq_email').on(table.email),
],
);
export const sysUserFieldDefinition = mysqlTable("sys_user_field_definition", {
fieldId: bigint("field_id", { mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
fieldName: varchar("field_name", { length: 255 }).notNull(),
fieldKey: varchar("field_key", { length: 255 }).notNull(),
fieldType: tinyint("field_type").notNull(),
dictModule: int("dict_module"),
isRequired: tinyint("is_required").default(0).notNull(),
limit: int(),
description: varchar({ length: 255 }),
defaultValue: varchar("default_value", { length: 255 }),
defaultOptions: varchar("default_options", { length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.fieldId], name: "sys_user_field_definition_field_id"}),
unique("uniq_field_name").on(table.fieldName),
unique("uniq_field_key").on(table.fieldKey),
]);
export const sysUserAuth = mysqlTable(
'sys_user_auth',
{
userId: bigint('user_id', { mode: 'number' }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
passwordModified: datetime('password_modified', { mode: 'string' }).notNull(),
passwordExpire: datetime('password_expire', { mode: 'string' }),
},
table => [primaryKey({ columns: [table.userId], name: 'sys_user_auth_user_id' })],
);
export const sysUserFieldValue = mysqlTable("sys_user_field_value", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
userId: bigint("user_id", { mode: "number" }).notNull(),
fieldId: int("field_id").notNull(),
value: varchar({ length: 4096 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_field_value_id"}),
unique("uniq_user_field").on(table.userId, table.fieldId),
]);
export const sysUserAuthHistory = mysqlTable(
'sys_user_auth_history',
{
id: bigint({ mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
modifiedAt: varchar('modified_at', { length: 255 }).notNull(),
},
table => [primaryKey({ columns: [table.id], name: 'sys_user_auth_history_id' })],
);
export const sysUserProfile = mysqlTable("sys_user_profile", {
id: bigint({ mode: "number" }).autoincrement().notNull(),
version: int().default(0).notNull(),
name: varchar({ length: 32 }).notNull(),
profileKey: varchar("profile_key", { length: 255 }).notNull(),
description: varchar({ length: 255 }),
content: varchar({ length: 255 }),
createdBy: bigint("created_by", { mode: "number" }).notNull(),
updatedBy: bigint("updated_by", { mode: "number" }).notNull(),
createdAt: datetime("created_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
updatedAt: datetime("updated_at", { mode: 'string'}).default(sql`(CURRENT_TIMESTAMP)`).notNull(),
},
(table) => [
primaryKey({ columns: [table.id], name: "sys_user_profile_id"}),
unique("uniq_name").on(table.name),
unique("uniq_profile_key").on(table.profileKey),
]);
export const sysUserFieldDefinition = mysqlTable(
'sys_user_field_definition',
{
fieldId: bigint('field_id', { mode: 'number' }).notNull(),
version: int().default(0).notNull(),
fieldName: varchar('field_name', { length: 255 }).notNull(),
fieldKey: varchar('field_key', { length: 255 }).notNull(),
fieldType: tinyint('field_type').notNull(),
dictModule: int('dict_module'),
isRequired: tinyint('is_required').default(0).notNull(),
limit: int(),
description: varchar({ length: 255 }),
defaultValue: varchar('default_value', { length: 255 }),
defaultOptions: varchar('default_options', { length: 255 }),
sort: int().default(0).notNull(),
status: int().notNull(),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.fieldId], name: 'sys_user_field_definition_field_id' }),
unique('uniq_field_name').on(table.fieldName),
unique('uniq_field_key').on(table.fieldKey),
],
);
export const sysUserFieldValue = mysqlTable(
'sys_user_field_value',
{
id: bigint({ mode: 'number' }).notNull(),
userId: bigint('user_id', { mode: 'number' }).notNull(),
fieldId: int('field_id').notNull(),
value: varchar({ length: 4096 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_user_field_value_id' }),
unique('uniq_user_field').on(table.userId, table.fieldId),
],
);
export const sysUserProfile = mysqlTable(
'sys_user_profile',
{
id: bigint({ mode: 'number' }).notNull(),
version: int().default(0).notNull(),
name: varchar({ length: 32 }).notNull(),
profileKey: varchar('profile_key', { length: 255 }).notNull(),
description: varchar({ length: 255 }),
content: varchar({ length: 255 }),
createdBy: bigint('created_by', { mode: 'number' }).notNull(),
updatedBy: bigint('updated_by', { mode: 'number' }).notNull(),
createdAt: datetime('created_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
updatedAt: datetime('updated_at', { mode: 'string' })
.default(sql`(CURRENT_TIMESTAMP)`)
.notNull(),
},
table => [
primaryKey({ columns: [table.id], name: 'sys_user_profile_id' }),
unique('uniq_name').on(table.name),
unique('uniq_profile_key').on(table.profileKey),
],
);

View File

@ -0,0 +1,22 @@
import fastifyPlugin from 'fastify-plugin';
async function constData(fastify, options) {
fastify.log.warn('Register CONSTDATA Plugin!');
const CONST = {
// 系统名称
SYSTEM_NAME: 'Fastify',
// 分布式锁前缀
DISTRIBUTED_LOCK_PREFIX: {
// 注册用户
REGISTER_USER: 'REGISTER_USER:',
}
}
fastify.decorate('const', CONST);
// 读取数据库基本信息
fastify.log.warn('Register CONSTDATA Complete!');
}
export default fastifyPlugin(constData);

View File

@ -23,6 +23,13 @@ async function database(fastify, options) {
// 启用此选项后MySQL驱动程序将在接收 bigint 或其他大数值时将其作为字符串返回而不是作为JavaScript数字。
// 这种处理方式可以避免JavaScript本身的数值精度限制问题确保大数值在应用程序中保持精确。
bigNumberStrings: true,
// typeCast: function (field, next) {
// if (field.type === 'LONGLONG') { // 处理 BIGINT 类型
// return field.string(); // 强制转换为字符串
// }
// return next();
// }
});
// 暴露数据库
const db = drizzle(pool);
@ -33,6 +40,8 @@ async function database(fastify, options) {
fastify.decorate('db', db);
// 读取数据库基本信息
fastify.log.warn('Register Database Complete! => DATABASE TABLES: ' + fastify.db.tableList.length);
fastify.log.warn(
'Register Database Complete! => DATABASE TABLES: ' + fastify.db.tableList.length,
);
}
export default fastifyPlugin(database);

View File

@ -1,12 +1,12 @@
import { hash } from 'bcrypt';
import fastifyPlugin from "fastify-plugin";
import fastifyPlugin from 'fastify-plugin';
// 加密
async function encryption(fastify, options) {
// 注册swagger插件新版配置方式
fastify.log.warn('Register Encryption Plugin!');
// 初始化时注册插件
fastify.decorate('hash', (str, salt = 10) => hash(str,salt));
fastify.decorate('hash', (str, salt = 10) => hash(str, salt));
fastify.log.warn('Register Encryption complete!');
}
export default fastifyPlugin(encryption);
export default fastifyPlugin(encryption);

View File

@ -1,52 +1,65 @@
import fastifyPlugin from 'fastify-plugin';
function errorHandler(fastify) {
// 添加 404 处理器 (关键修改)
fastify.setNotFoundHandler((request, reply) => {
const error = new Error(`路由 ${request.url} 不存在`);
error.code = 'ROUTE_NOT_FOUND';
error.statusCode = 404;
return reply.code(404).send(error);
});
fastify.setErrorHandler((error, request, reply) => {
// 记录原始错误
fastify.log.error(`[ErrorHandler] ${error.message}`, error);
// fastify.log.error(`[ErrorHandler] ${error.message}`, error);
console.log('AAAA1validation', error.validation);
console.log('AAAA2statusCode', error.statusCode);
console.log('AAAA3code', error.code);
console.log('AAAA4message', error.message);
// 保留AJV验证错误处理
if (error.validation) {
console.log(error)
const bodyError = error.validation.find(e =>
e.keyword === 'type' ||
e.keyword === 'required' && e.validationContext == 'body'
// 没有请求体
const bodyError = error.validation.find(
e =>
e.keyword === 'type' ||
(e.keyword === 'required' && e.validationContext == 'body'),
);
if (bodyError) {
return reply.code(400).send({
error: '请求体不能为空请提供有效的JSON数据',
details: error.validation
code: error.statusCode,
codeString: error.code,
error: `请求体不能为空请提供有效的JSON数据`,
details: error.validation,
});
}else{
return reply.code(400).send({
code: error.statusCode,
codeString: error.code,
error: error.message,
details: error.validation,
});
}
return reply.code(400).send({
error: error.message,
details: error.validation
});
}
// 使用sensible的HTTP错误处理
if (error.statusCode && error.statusCode >= 400) {
return reply
.code(error.statusCode)
.send({
code: error.code || 'HTTP_ERROR',
error: error.message
});
}
// 处理Fastify自动生成的404错误
if (error.code === 'FST_ERR_NOT_FOUND') {
return reply.code(404).send({
code: 'ROUTE_NOT_FOUND',
error: `路由 ${request.url} 不存在`
return reply.code(error.statusCode).send({
code: error.statusCode,
codeString: error.code,
error: error.message,
...(error.details && { details: error.details }),
});
}
// 最终未处理错误(生产环境隐藏细节)
const isProduction = process.env.NODE_ENV === 'production';
reply.code(500).send({
code: 'SERVER_ERROR',
code: '500',
error: isProduction ? '服务器内部错误' : error.message,
...(!isProduction && { stack: error.stack })
...(!isProduction && { stack: error.stack }),
});
});
}
export default fastifyPlugin(errorHandler);
export default fastifyPlugin(errorHandler);

View File

@ -3,6 +3,9 @@ import database from '#plugins/database/index';
import swagger from '#plugins/swagger/index';
import errorHandler from '#plugins/error/errorHandler';
import encryption from '#plugins/encryption/index';
import snowflake from '#plugins/snowflake/index';
import redis from '#plugins/redis/index';
import conseData from '#plugins/const/index';
async function plugin(fastify, opts) {
fastify.log.warn('Register Global Plugin!');
@ -10,16 +13,19 @@ async function plugin(fastify, opts) {
fastify.decorate('config', opts.config);
// 注册数据库
await fastify.register(database);
// 注册redis
await fastify.register(redis);
// 注册swagger
fastify.config.env === 'development' && await fastify.register(swagger);
fastify.config.env === 'development' && (await fastify.register(swagger));
// 注册加密工具
await fastify.register(encryption);
// 注册雪花ID
await fastify.register(snowflake);
// 注册常量
await fastify.register(conseData);
// 注册错误处理工具
await fastify.register(import('@fastify/sensible'));
fastify.register(errorHandler)
fastify.register(errorHandler);
fastify.log.warn('Register Global Plugin complete!');
}

View File

@ -0,0 +1,31 @@
import fastifyPlugin from 'fastify-plugin';
import { createClient } from 'redis';
async function redis(fastify, options) {
fastify.log.warn('Register Redis Plugin!');
const config = fastify.config.redis;
const redisConnect = createClient({
name: config.connectName,
username: config.username,
password: config.password,
database: config.database,
url: `redis://${config.username}:${config.password}@${config.host}:${config.port}/${config.database}`,
});
redisConnect.on('connect', async () => {
fastify.log.info(await redisConnect.set('SI HI', 'YuHeng'));
});
redisConnect.on('error', (err) => {
console.log(err)
fastify.log.error('Redis error: ', err);
});
// 连接到 Redis
await redisConnect.connect();
fastify.decorate('redis', redisConnect);
// 读取数据库基本信息
fastify.log.warn('Register Redis Complete! => REDIS SI HI: ' + await redisConnect.get('SI HI'));
}
export default fastifyPlugin(redis);

View File

@ -0,0 +1,73 @@
import fastifyPlugin from 'fastify-plugin';
// 雪花ID
class Snowflake {
constructor({ workerId = 0n, dataCenterId = 0n, sequence = 0n } = {}) {
this.workerIdBits = 3n; // 工作节点ID位数
this.dataCenterIdBits = 6n; // 数据中心ID位数
this.sequenceBits = 12n; // 序列号位数
this.maxWorkerId = -1n ^ (-1n << this.workerIdBits);
this.maxDataCenterId = -1n ^ (-1n << this.dataCenterIdBits);
this.workerIdShift = this.sequenceBits;
this.dataCenterIdShift = this.sequenceBits + this.workerIdBits;
this.timestampShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;
this.sequenceMask = -1n ^ (-1n << this.sequenceBits);
this.epoch = 1577836800000n; // 2020-01-01 00:00:00 UTC
this.lastTimestamp = -1n;
this.sequence = sequence;
this.workerId = BigInt(workerId);
this.dataCenterId = BigInt(dataCenterId);
if (this.workerId > this.maxWorkerId || this.workerId < 0n) {
throw new Error(`Worker ID must be between 0 and ${this.maxWorkerId}`);
}
if (this.dataCenterId > this.maxDataCenterId || this.dataCenterId < 0n) {
throw new Error(`Data Center ID must be between 0 and ${this.maxDataCenterId}`);
}
}
nextId() {
let timestamp = BigInt(Date.now());
if (timestamp < this.lastTimestamp) {
throw new Error('Clock moved backwards!');
}
if (this.lastTimestamp === timestamp) {
this.sequence = (this.sequence + 1n) & this.sequenceMask;
if (this.sequence === 0n) {
while (timestamp <= this.lastTimestamp) {
timestamp = BigInt(Date.now());
}
}
} else {
this.sequence = 0n;
}
this.lastTimestamp = timestamp;
return (
((timestamp - this.epoch) << this.timestampShift) |
(this.dataCenterId << this.dataCenterIdShift) |
(this.workerId << this.workerIdShift) |
this.sequence
);
}
}
async function snowflake(fastify, options) {
// 注册swagger插件新版配置方式
fastify.log.warn('Register Snowflake Plugin!');
// 初始化生成器工作节点ID范围 0-31
const generator = new Snowflake({
workerId: BigInt(fastify.config.deviceInfo.machineId), // 工作节点ID
dataCenterId: BigInt(fastify.config.deviceInfo.dataCenterId), // 数据中心ID
});
// 初始化时注册插件
fastify.decorate('snowflake', () => generator.nextId().toString());
fastify.log.warn('Register Snowflake complete!');
}
export default fastifyPlugin(snowflake);

View File

@ -1,6 +1,6 @@
import fastifyPlugin from "fastify-plugin";
import fastifySwagger from "@fastify/swagger";
import fastifySwaggerUI from "@fastify/swagger-ui";
import fastifyPlugin from 'fastify-plugin';
import fastifySwagger from '@fastify/swagger';
import fastifySwaggerUI from '@fastify/swagger-ui';
async function swagger(fastify, options) {
// 注册swagger插件新版配置方式
fastify.log.warn('Register Swagger Plugin!');
@ -9,18 +9,20 @@ async function swagger(fastify, options) {
info: {
title: `${fastify.config.serviceName} API`,
version: fastify.config.version,
description: fastify.config.description
description: fastify.config.description,
},
},
// staticCSP: true, // 启用安全策略
exposeRoute: true // 自动暴露路由
exposeRoute: true, // 自动暴露路由
});
// 注册swagger-ui界面 (新增部分)
await fastify.register(fastifySwaggerUI, {
routePrefix: '/docs',
});
fastify.log.warn('Register Swagger complete!');
fastify.log.info(`Swagger documentation available at http://127.0.0.1:${fastify.config.server.port}/docs`);
fastify.log.info(
`Swagger documentation available at http://127.0.0.1:${fastify.config.server.port}/docs`,
);
}
export default fastifyPlugin(swagger);
export default fastifyPlugin(swagger);

View File

@ -1,29 +1,18 @@
import { testSchema } from "#src/schema/test.schema";
import { testService } from "#src/services/test.service";
import userRoute from "#routes/user.route";
import { testSchema } from '#src/schema/test.schema';
import { testService } from '#src/services/test.service';
import userRoute from '#routes/user.route';
export default async function routes(fastify, options) {
// 定义一个GET请求的路由路径为根路径
fastify.get('/', async (request, reply) => {
fastify.log.info(fastify.config);
return fastify.config;
return fastify.config.deviceInfo;
});
fastify.get('/test', testSchema , testService);
fastify.get('/test', testSchema, testService);
// 注册子路由
fastify.register(userRoute, { prefix: '/user' });
fastify.route({
method: 'POST',
url: '/login',
@ -31,15 +20,14 @@ export default async function routes(fastify, options) {
tags: ['认证体系'], // 接口分组
summary: '用户登录',
description: '使用用户名密码进行身份认证',
security: [{
}],
security: [{}],
body: {
type: 'object',
required: ['username', 'password'],
properties: {
username: { type: 'string', default: 'user1' },
password: { type: 'string', default: '123456' }
}
password: { type: 'string', default: '123456' },
},
},
response: {
200: {
@ -47,22 +35,22 @@ export default async function routes(fastify, options) {
type: 'object',
properties: {
token: { type: 'string' },
expire: { type: 'number' }
}
expire: { type: 'number' },
},
},
401: {
description: '认证失败',
type: 'object',
properties: {
code: { type: 'number', enum: [401] },
error: { type: 'string' }
}
}
}
error: { type: 'string' },
},
},
},
},
handler: async (req, reply) => {
// ...业务逻辑...
}
},
});
// 输出路由树

View File

@ -1,27 +1,17 @@
import { userDefaultSchema, userRegisterSchema } from "#src/schema/user.schema";
import { userDefaultService, } from "#src/services/user.service";
import {userRegisterService} from "#src/services/user/user.service"
import { userDefaultSchema, userRegisterSchema } from '#src/schema/user.schema';
import { userDefaultService } from '#src/services/user.service';
import { userRegisterService } from '#src/services/user/user.service';
export default async function userRoute(fastify, options) {
// 新用户注册
fastify.post('/register', {
schema: userRegisterSchema,
}, userRegisterService);
fastify.post('/register', { schema: userRegisterSchema }, userRegisterService);
fastify.get('/', userDefaultSchema, userDefaultService);
/**
*
* 个人接口
*
*/
/**
*
* 个人接口
*
*/
// 注册
// 登录
fastify.post('/login', userDefaultSchema, userDefaultService);
@ -33,11 +23,11 @@ export default async function userRoute(fastify, options) {
fastify.get('/getUserInfo', userDefaultSchema, userDefaultService);
// 修改当前用户信息
fastify.post('/updateUserInfo', userDefaultSchema, userDefaultService);
/**
*
* 管理接口
*
*/
/**
*
* 管理接口
*
*/
// 获取用户列表
@ -48,20 +38,18 @@ export default async function userRoute(fastify, options) {
// 更新用户信息
// 批量删除用户
// 批量重置用户密码
// 批量启用用户
// 批量禁用用户
/**
*
* 拓展字段
*
*/
/**
*
* 拓展字段
*
*/
// 获取拓展字段列表
}
}

View File

@ -1,5 +1,5 @@
import { isTrim } from "#utils/ajv/method";
import { is } from "drizzle-orm";
import { isTrim } from '#utils/ajv/method';
import { is } from 'drizzle-orm';
// 原子schema定义
export const username = {
@ -12,8 +12,8 @@ export const username = {
errorMessage: {
minLength: '用户名至少需要4个字符',
maxLength: '用户名不能超过20个字符',
pattern: '必须全部小写,且只能包含字母、数字和下划线,并以字母开头'
}
pattern: '必须全部小写,且只能包含字母、数字和下划线,并以字母开头',
},
};
export const email = {
@ -22,8 +22,8 @@ export const email = {
maxLength: 128,
errorMessage: {
format: '邮箱格式不正确',
maxLength: '邮箱不能超过128个字符'
}
maxLength: '邮箱不能超过128个字符',
},
};
export const password = {
@ -34,6 +34,6 @@ export const password = {
errorMessage: {
minLength: '密码长度至少8位',
maxLength: '密码不能超过128个字符',
pattern: '必须包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符'
}
};
pattern: '必须包含至少一个小写字母、一个大写字母、一个数字和一个特殊字符',
},
};

View File

@ -6,26 +6,26 @@ export const testSchema = {
type: 'object',
required: ['username', 'password'],
properties: {
username: { type:'string', default: 'user1' },
password: { type:'string', default: '123456' }
}
username: { type: 'string', default: 'user1' },
password: { type: 'string', default: '123456' },
},
},
response: {
200: {
description: '登录成功',
type: 'object',
properties: {
token: { type:'string' },
expire: { type: 'number' }
}
token: { type: 'string' },
expire: { type: 'number' },
},
},
401: {
description: '认证失败',
type: 'object',
properties: {
code: { type: 'number', enum: [401] },
error: { type:'string' }
}
}
}
}
error: { type: 'string' },
},
},
},
};

View File

@ -1,8 +1,6 @@
import { email, username, password } from "#schema/atomSchema";
import { email, username, password } from '#schema/atomSchema';
// 默认schema
export const userDefaultSchema = {
}
export const userDefaultSchema = {};
// 新用户注册
export const userRegisterSchema = {
@ -22,18 +20,67 @@ export const userRegisterSchema = {
properties: {
username,
email,
password
}
password,
},
},
response: {
201: {
description: '注册成功',
type: 'object',
properties: {
userId: { type: 'number', example: 1001 },
username: { type: 'string' },
email: { type: 'string' },
createdAt: { type: 'string', format: 'date-time' }
code: { type: 'number' },
data: {
type: 'object',
properties: {
username: { type: 'string' },
email: { type: 'string' },
userId: {
type: 'string',
examples: ["692397256422330400"]
},
pid: {
type: 'string',
enum: [0] // 固定值
},
phone: {
type: ['string', 'null'],
default: null
},
avatarUrl: {
type: ['string', 'null'],
default: null
},
userType: {
type: ['number', 'null'],
default: null
},
status: {
type: 'number',
enum: [0, 1] // 假设 0-正常 1-禁用
},
createdBy: {
type: 'number',
enum: [0] // 个人注册固定值
},
updatedBy: {
type: ['number', 'null'],
default: null
},
createdAt: {
type: 'string',
format: 'date-time'
},
updatedAt: {
type: 'string',
format: 'date-time'
}
},
required: [
'username', 'email', 'userId', 'pid',
'status', 'createdBy', 'createdAt', 'updatedAt'
],
additionalProperties: false // 严格模式
},
message: { type: 'string' }
}
},
400: {
@ -41,17 +88,17 @@ export const userRegisterSchema = {
type: 'object',
properties: {
code: { type: 'number', enum: [400] },
error: { type: 'string' }
}
error: { type: 'string' },
},
},
409: {
description: '用户名/邮箱已存在',
type: 'object',
properties: {
code: { type: 'number', enum: [409] },
error: { type: 'string' }
}
}
error: { type: 'string' },
},
},
},
security: [{}] // 不需要认证
};
security: [{}], // 不需要认证
};

View File

@ -1,6 +1,6 @@
export async function testService(request, reply) {
this.log.warn('testService');
return {
test: 'test'
}
}
test: 'test',
};
}

View File

@ -6,7 +6,7 @@ export async function userDefaultService() {
export async function userRegisterService(request, reply) {
// 1. 获取已验证的参数
const { username, email, password } = req.body;
// 2. 业务逻辑校验
const exists = await fastify.db.users.checkExists({ username, email });
if (exists) {
@ -25,6 +25,6 @@ export async function userRegisterService(request, reply) {
userId: newUser.id,
username: newUser.username,
email: newUser.email,
createdAt: newUser.created_at.toISOString()
createdAt: newUser.created_at.toISOString(),
});
}
}

View File

@ -1,24 +1,32 @@
import { sysUser } from "#entity";
import { or, eq } from "drizzle-orm";
import { sysUser } from '#entity';
import { or, eq } from 'drizzle-orm';
// 查重用户名和邮箱
export async function checkExistsUsernameAndEmail(username, email) {
const result = await this.db
.select()
.from(sysUser)
.where(or(
eq(sysUser.username, username), // 明确指定字段比较
eq(sysUser.email, email)
));
return result.length > 0; // 正确判断结果集是否为空
.where(
or(
eq(sysUser.username, username), // 明确指定字段比较
eq(sysUser.email, email),
),
);
return result.length > 0; // 正确判断结果集是否为空
}
// 个人注册账户
export async function registerAccountForMyself(userData) {
const [newUser] = await this.db.insert(sysUser)
.values({
username: userData.username,
email: userData.email,
password: userData.password, // 实际应存储加密后的密码
})
const result = await this.db.insert(sysUser).values({
username: userData.username,
email: userData.email,
password: userData.password, // 实际应存储加密后的密码
userId: userData.userId, // 使用雪花算法生成唯一ID
pid: 0, // 个人注册账户父节点ID为0
createdBy: 0, // 个人注册账户创建者ID为0
})
const [newUser] = await this.db
.select()
.from(sysUser)
.where(eq(sysUser.userId, userData.userId));
return newUser;
}
}

View File

@ -1,23 +1,59 @@
import { checkExistsUsernameAndEmail, registerAccountForMyself } from "#services/user/user.db";
import { checkExistsUsernameAndEmail, registerAccountForMyself } from '#services/user/user.db';
// 新用户注册
export async function userRegisterService(request, reply) {
// 1. 获取已验证的参数
const { username, email, password } = request.body;
// 2. 检测用户名和邮箱是否已存在
const exists = await checkExistsUsernameAndEmail.call(this,username, email);
if (exists) {
throw reply.code(409).send({
code: 'USER_EXISTS',
message: '用户名或邮箱已存在'
});
// 2. 分布式锁
// 生成分布式锁 key
const lockKey = `${this.const.DISTRIBUTED_LOCK_PREFIX.REGISTER_USER}${username}:${email}`;
const userId = this.snowflake();
const lockIdentifier = userId;
let renewInterval;
try {
// 尝试获取分布式锁NX: 仅当key不存在时设置EX: 过期时间5秒
const locked = await this.redis.SET(lockKey, lockIdentifier, { NX: true, EX: 5 });
if (!locked) {
throw this.httpErrors.tooManyRequests('操作正在进行,请稍后重试');
}
// 启动续锁定时器每3秒续期一次
renewInterval = setInterval(async () => {
const currentVal = await this.redis.GET(lockKey);
if (currentVal === lockIdentifier) {
await this.redis.EXPIRE(lockKey, 5); // 重置过期时间
}
}, 3000);
await new Promise(resolve => setTimeout(resolve, 3000));
// 2. 检测用户名和邮箱是否已存在
const exists = await checkExistsUsernameAndEmail.call(this, username, email);
if (exists) {
throw this.httpErrors.conflict('用户名或邮箱已存在');
}
// 3. 注册用户
const newUser = await registerAccountForMyself.call(this, {
username,
email,
password: await this.hash(password),
userId
});
reply.code(201) // <-- 关键修改
.send({
code: 201,
data: newUser,
message: '用户创建成功'
});
} finally {
if (renewInterval) clearInterval(renewInterval);
// 释放锁逻辑移除Lua脚本
const currentVal = await this.redis.GET(lockKey);
if (currentVal === lockIdentifier) {
await this.redis.DEL(lockKey);
}
}
// 3. 注册用户
await registerAccountForMyself.call(this,{
username,
email,
password: await this.hash(password)
});
}
}

View File

@ -1,16 +1,16 @@
import ajvErrors from "ajv-errors";
import { isLowerCase, isTrim } from "#utils/ajv/method";
import ajvErrors from 'ajv-errors';
import { isLowerCase, isTrim } from '#utils/ajv/method';
export const ajvConfig = {
customOptions: {
removeAdditional: true, // 自动删除schema未定义的额外属性
useDefaults: true, // 使用schema中定义的默认值填充缺失字段
coerceTypes: true, // 自动转换数据类型(如字符串"123"→数字123
allErrors: true, // 收集所有验证错误(不止第一个错误)
allowUnionTypes: true // 允许联合类型(如 type: ["string", "number"]
removeAdditional: true, // 自动删除schema未定义的额外属性
useDefaults: true, // 使用schema中定义的默认值填充缺失字段
coerceTypes: true, // 自动转换数据类型(如字符串"123"→数字123
allErrors: true, // 收集所有验证错误(不止第一个错误)
allowUnionTypes: true, // 允许联合类型(如 type: ["string", "number"]
},
plugins: [
ajvErrors, // 支持自定义错误消息
isLowerCase, // 自定义验证:强制小写(如用户名)
isTrim, // 自定义验证自动trim字符串
]
}
ajvErrors, // 支持自定义错误消息
isLowerCase, // 自定义验证:强制小写(如用户名)
isTrim, // 自定义验证自动trim字符串
],
};

View File

@ -14,8 +14,6 @@ import Ajv from 'ajv';
// ajv错误消息回应
import ajvErrors from 'ajv-errors';
// // 将字符串转化为全小写
// export function isLowerCase(ajv){
// ajv.addKeyword({
@ -63,21 +61,19 @@ import ajvErrors from 'ajv-errors';
export function isLowerCase(ajv) {
ajv.addKeyword({
keyword: 'isLowerCase',
validate: (schema, data) => typeof data === 'string' && data === data.toLowerCase()
validate: (schema, data) => typeof data === 'string' && data === data.toLowerCase(),
});
}
export function isTrim(ajv) {
ajv.addKeyword({
keyword: 'isTrim',
validate: (schema, data) => typeof data === 'string' && data === data.trim()
validate: (schema, data) => typeof data === 'string' && data === data.trim(),
});
}
// 给fastify添加自定义的参数校验规则
function customAjv(fastify, options){
function customAjv(fastify, options) {
// 创建一个新的 AJV 实例
// 创建一个新的 AJV 实例
const ajv = new Ajv({
@ -85,7 +81,7 @@ function customAjv(fastify, options){
useDefaults: true,
coerceTypes: true,
// jsonPointers: true,
allErrors: true
allErrors: true,
});
ajvErrors(ajv);
isLowerCase(ajv);
@ -94,4 +90,3 @@ function customAjv(fastify, options){
return ajv.compile(schema);
});
}

View File

@ -1,43 +1,45 @@
import config from '#config/index';
export default {
// 日志级别顺序(从高到低):
// fatal(致命) > error(错误) > warn(警告) > info(信息) > debug(调试) > trace(追踪)
// 高级别日志会包含低级别日志设置warn会包含error和fatal
level: config.logger.level,
transport: {
// 修正为独立的多传输配置
targets: [
config.logger.console && {
level: config.logger.level,
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'yyyy-mm-dd HH:MM:ss',
ignore: 'pid,hostname,reqId,res,responseTime',
// 可用字段列表:
// - pid: 进程ID
// - hostname: 主机名
// - level: 日志级别(不建议忽略)
// - time: 时间戳需配合translateTime使用
// - msg/message: 日志消息(必须保留)
// - module: 模块名称(自定义字段)
// - name: 日志名称
// - v: pino版本号
// - reqId: 请求ID
// - res: 响应对象
// - req: 请求对象
// - responseTime: 响应时间
// - 支持通配符如 'req*'(忽略所有 req 开头字段)
export default function loggerConfig(config) {
return {
// 日志级别顺序(从高到低):
// fatal(致命) > error(错误) > warn(警告) > info(信息) > debug(调试) > trace(追踪)
// 高级别日志会包含低级别日志设置warn会包含error和fatal
level: config.logger.level,
base: { machineId: config.deviceInfo.machineId },
transport: {
// 修正为独立的多传输配置
targets: [
config.logger.console && {
level: config.logger.level,
target: 'pino-pretty',
options: {
colorize: true,
translateTime: 'yyyy-mm-dd HH:MM:ss',
ignore: 'hostname,reqId,',
// 可用字段列表:
// - pid: 进程ID
// - hostname: 主机名
// - level: 日志级别(不建议忽略)
// - time: 时间戳需配合translateTime使用
// - msg/message: 日志消息(必须保留)
// - module: 模块名称(自定义字段)
// - name: 日志名称
// - v: pino版本号
// - reqId: 请求ID
// - res: 响应对象
// - req: 请求对象
// - responseTime: 响应时间
// - 支持通配符如 'req*'(忽略所有 req 开头字段)
},
},
},
{
target: 'pino/file',
options: {
destination: config.logger.filePath, // 文件路径从配置读取
mkdir: true,
ignore: 'pid,hostname',
{
target: 'pino/file',
options: {
destination: config.logger.filePath, // 文件路径从配置读取
mkdir: true,
ignore: 'pid,hostname',
},
},
},
],
},
};
],
},
};
}

View File

@ -2,11 +2,14 @@ const text = '> Si Hi <'; // 需要显示的文本
const terminalWidth = process.stdout.columns || 100;
const padding = Math.max(0, Math.floor((terminalWidth - text.length * 1.5) / 2)); // 中文每个字占2字符宽度
/* eslint-disable no-console */
console.log(
'\x1B[48;5;0m%s\x1B[0m', // 灰色背景
'\x1B[32;5;12m\x1B[1m ' + // 白色加粗
'-'.repeat(padding) +
text +
'-'.repeat(padding) +
' \x1B[0m', // 重置样式
);
export default function echo() {
console.log(
'\x1B[48;5;0m%s\x1B[0m', // 灰色背景
'\x1B[32;5;12m\x1B[1m ' + // 白色加粗
'-'.repeat(padding) +
text +
'-'.repeat(padding) +
' \x1B[0m', // 重置样式
);
}

View File

@ -9,7 +9,7 @@ CREATE TABLE `sys_dict` (
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` int NOT NULL COMMENT '状态 0启用 1禁用',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -26,7 +26,7 @@ CREATE TABLE `sys_organization` (
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` int NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`org_id`),
@ -43,7 +43,7 @@ CREATE TABLE `sys_organization_manager` (
`status` int NOT NULL COMMENT '状态',
`description` varchar(255) NULL,
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -64,7 +64,7 @@ CREATE TABLE `sys_permission` (
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` int NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`perm_id`),
@ -77,7 +77,7 @@ CREATE TABLE `sys_re_role_permission` (
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`perm_id` BIGINT NOT NULL COMMENT '权限ID',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -90,7 +90,7 @@ CREATE TABLE `sys_re_user_organization` (
`org_id` BIGINT NOT NULL COMMENT '组织ID',
`version` INT NOT NULL DEFAULT 0,
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -103,7 +103,7 @@ CREATE TABLE `sys_re_user_role` (
`role_id` BIGINT NOT NULL COMMENT '角色ID',
`version` INT NOT NULL DEFAULT 0,
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -118,7 +118,7 @@ CREATE TABLE `sys_role` (
`description` varchar(255) NULL COMMENT '角色描述',
`status` int NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`role_id`),
@ -135,7 +135,7 @@ CREATE TABLE `sys_user` (
`user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型从字典取',
`status` tinyint DEFAULT 0 NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
@ -174,7 +174,7 @@ CREATE TABLE `sys_user_field_definition` (
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` int NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`field_id`),
@ -188,7 +188,7 @@ CREATE TABLE `sys_user_field_value` (
`field_id` int NOT NULL COMMENT '字段ID',
`value` varchar(4096) NULL COMMENT '用户拓展字段值',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -203,7 +203,7 @@ CREATE TABLE `sys_profile` (
`description` varchar(255) NULL COMMENT '系统配置记录描述',
`content` varchar(255) NULL COMMENT '系统配置记录值',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
@ -220,7 +220,7 @@ CREATE TABLE `sys_module` (
`sort` int NOT NULL DEFAULT 0 COMMENT '排序',
`status` int NOT NULL COMMENT '状态',
`created_by` bigint NOT NULL COMMENT '创建人',
`updated_by` bigint NOT NULL COMMENT '更新人',
`updated_by` bigint NULL COMMENT '更新人',
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),