diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/convert/SystemUserConvert.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/convert/SystemUserConvert.java index 4e5fea63..8c9000a1 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/convert/SystemUserConvert.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/convert/SystemUserConvert.java @@ -19,8 +19,8 @@ import org.dromara.visor.framework.common.security.LoginUser; import org.dromara.visor.module.infra.entity.domain.SystemUserDO; import org.dromara.visor.module.infra.entity.dto.UserInfoDTO; import org.dromara.visor.module.infra.entity.request.user.*; +import org.dromara.visor.module.infra.entity.vo.SystemUserBaseVO; import org.dromara.visor.module.infra.entity.vo.SystemUserVO; -import org.dromara.visor.module.infra.entity.vo.UserCollectInfoVO; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; @@ -58,6 +58,6 @@ public interface SystemUserConvert { UserInfoDTO toUserInfo(SystemUserDO domain); - UserCollectInfoVO toCollectInfo(LoginUser user); + SystemUserBaseVO toBase(SystemUserDO user); } diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/domain/SystemUserDO.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/domain/SystemUserDO.java index c4644954..7e395333 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/domain/SystemUserDO.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/domain/SystemUserDO.java @@ -75,6 +75,14 @@ public class SystemUserDO extends BaseDO { @TableField("status") private Integer status; + @Schema(description = "修改密码状态") + @TableField("update_password_status") + private Integer passwordUpdateStatus; + + @Schema(description = "修改密码原因") + @TableField("update_password_reason") + private String passwordUpdateReason; + @Schema(description = "最后登录时间") @TableField("last_login_time") private Date lastLoginTime; diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserCollectInfoVO.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/SystemUserBaseVO.java similarity index 78% rename from orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserCollectInfoVO.java rename to orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/SystemUserBaseVO.java index c46ffed7..608e52fe 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserCollectInfoVO.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/SystemUserBaseVO.java @@ -21,9 +21,6 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.List; -import java.util.Map; - /** * 用户基本信息 视图响应对象 * @@ -35,8 +32,8 @@ import java.util.Map; @Builder @NoArgsConstructor @AllArgsConstructor -@Schema(name = "UserCollectInfoVO", description = "用户聚合信息 视图响应对象") -public class UserCollectInfoVO { +@Schema(name = "SystemUserBaseVO", description = "用户基本信息 视图响应对象") +public class SystemUserBaseVO { @Schema(description = "id") private Long id; @@ -50,10 +47,10 @@ public class UserCollectInfoVO { @Schema(description = "头像地址") private String avatar; - @Schema(description = "系统偏好") - private Map systemPreference; + @Schema(description = "修改密码状态") + private Integer passwordUpdateStatus; - @Schema(description = "已经提示的key") - private List tippedKeys; + @Schema(description = "修改密码原因") + private String passwordUpdateReason; } diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserPermissionVO.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserPermissionVO.java index 9d455cb6..e587e499 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserPermissionVO.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/entity/vo/UserPermissionVO.java @@ -23,6 +23,7 @@ import lombok.NoArgsConstructor; import java.util.Collection; import java.util.List; +import java.util.Map; /** * 用户权限 视图响应对象 @@ -38,8 +39,8 @@ import java.util.List; @Schema(name = "UserPermissionVO", description = "用户权限 视图响应对象") public class UserPermissionVO { - @Schema(description = "用户聚合信息") - private UserCollectInfoVO user; + @Schema(description = "用户信息") + private SystemUserBaseVO user; @Schema(description = "该用户已启用的角色") private Collection roles; @@ -47,4 +48,10 @@ public class UserPermissionVO { @Schema(description = "该用户已启用的权限") private List permissions; + @Schema(description = "系统偏好") + private Map systemPreference; + + @Schema(description = "已经提示的key") + private List tippedKeys; + } diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordReasonEnum.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordReasonEnum.java new file mode 100644 index 00000000..5f97df70 --- /dev/null +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordReasonEnum.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 - present Jiahang Li (visor.orionsec.cn ljh1553488six@139.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.infra.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 更新密码原因 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/12/11 14:37 + */ +@Getter +@AllArgsConstructor +public enum UpdatePasswordReasonEnum { + + /** + * 新用户 + */ + NEW, + +} diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordStatusEnum.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordStatusEnum.java new file mode 100644 index 00000000..c3408794 --- /dev/null +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/enums/UpdatePasswordStatusEnum.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 - present Jiahang Li (visor.orionsec.cn ljh1553488six@139.com). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.dromara.visor.module.infra.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 更新密码状态 + * + * @author Jiahang Li + * @version 1.0.0 + * @since 2024/12/11 14:37 + */ +@Getter +@AllArgsConstructor +public enum UpdatePasswordStatusEnum { + + /** + * 无需修改 + */ + NO_REQUIRE(0), + + /** + * 需要修改 + */ + REQUIRED(1), + + ; + + private final Integer status; + +} diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java index b5f5ce84..f0be2469 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/SystemUserServiceImpl.java @@ -49,6 +49,8 @@ import org.dromara.visor.module.infra.entity.domain.SystemUserDO; import org.dromara.visor.module.infra.entity.dto.UserInfoDTO; import org.dromara.visor.module.infra.entity.request.user.*; import org.dromara.visor.module.infra.entity.vo.SystemUserVO; +import org.dromara.visor.module.infra.enums.UpdatePasswordReasonEnum; +import org.dromara.visor.module.infra.enums.UpdatePasswordStatusEnum; import org.dromara.visor.module.infra.enums.UserStatusEnum; import org.dromara.visor.module.infra.service.*; import org.springframework.scheduling.annotation.Async; @@ -118,6 +120,8 @@ public class SystemUserServiceImpl implements SystemUserService { this.checkNicknamePresent(record); // 加密密码 record.setPassword(Signatures.md5(request.getPassword())); + record.setPasswordUpdateStatus(UpdatePasswordStatusEnum.REQUIRED.getStatus()); + record.setPasswordUpdateReason(UpdatePasswordReasonEnum.NEW.name()); // 插入 int effect = systemUserDAO.insert(record); log.info("SystemUserService-createSystemUser effect: {}, record: {}", effect, JSON.toJSONString(record)); @@ -309,6 +313,8 @@ public class SystemUserServiceImpl implements SystemUserService { SystemUserDO update = new SystemUserDO(); update.setId(id); update.setPassword(Signatures.md5(request.getPassword())); + update.setPasswordUpdateStatus(UpdatePasswordStatusEnum.NO_REQUIRE.getStatus()); + update.setPasswordUpdateReason(Const.EMPTY); int effect = systemUserDAO.updateById(update); log.info("SystemUserService-resetPassword record: {}, effect: {}", JSON.toJSONString(update), effect); // 删除登录失败次数缓存 diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/UserPermissionServiceImpl.java b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/UserPermissionServiceImpl.java index 8dd61e84..15cc4ecf 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/UserPermissionServiceImpl.java +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/java/org/dromara/visor/module/infra/service/impl/UserPermissionServiceImpl.java @@ -30,13 +30,14 @@ import org.dromara.visor.module.infra.convert.SystemUserConvert; import org.dromara.visor.module.infra.dao.SystemMenuDAO; import org.dromara.visor.module.infra.dao.SystemRoleDAO; import org.dromara.visor.module.infra.dao.SystemRoleMenuDAO; +import org.dromara.visor.module.infra.dao.SystemUserDAO; import org.dromara.visor.module.infra.define.RoleDefine; import org.dromara.visor.module.infra.entity.domain.SystemMenuDO; import org.dromara.visor.module.infra.entity.domain.SystemRoleDO; import org.dromara.visor.module.infra.entity.domain.SystemRoleMenuDO; +import org.dromara.visor.module.infra.entity.domain.SystemUserDO; import org.dromara.visor.module.infra.entity.dto.SystemMenuCacheDTO; import org.dromara.visor.module.infra.entity.vo.SystemMenuVO; -import org.dromara.visor.module.infra.entity.vo.UserCollectInfoVO; import org.dromara.visor.module.infra.entity.vo.UserPermissionVO; import org.dromara.visor.module.infra.enums.MenuStatusEnum; import org.dromara.visor.module.infra.enums.MenuTypeEnum; @@ -76,6 +77,9 @@ public class UserPermissionServiceImpl implements UserPermissionService { @Getter private final Map> roleMenuCache = new HashMap<>(); + @Resource + private SystemUserDAO systemUserDAO; + @Resource private SystemRoleDAO systemRoleDAO; @@ -229,43 +233,24 @@ public class UserPermissionServiceImpl implements UserPermissionService { @Override public UserPermissionVO getUserPermission() { // 获取用户信息 - UserCollectInfoVO user = SystemUserConvert.MAPPER.toCollectInfo(SecurityUtils.getLoginUser()); - Long id = user.getId(); + Long userId = SecurityUtils.getLoginUserId(); // 获取用户系统偏好 - Future> systemPreference = preferenceService.getPreferenceAsync(id, PreferenceTypeEnum.SYSTEM); + Future> systemPreference = preferenceService.getPreferenceAsync(userId, PreferenceTypeEnum.SYSTEM); + // 查询用户信息 + SystemUserDO user = systemUserDAO.selectById(userId); // 获取用户角色 Map roles = this.getUserEnabledRoles(); - // 获取用户权限 - List permissions; - if (roles.isEmpty()) { - permissions = Lists.empty(); - } else { - if (RoleDefine.containsAdmin(roles.values())) { - // 管理员拥有全部权限 - permissions = Lists.of(Const.ASTERISK); - } else { - // 当前用户所适配的角色的权限 - permissions = roles.keySet() - .stream() - .map(roleMenuCache::get) - .filter(Objects::nonNull) - .flatMap(Collection::stream) - .filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus())) - .map(SystemMenuCacheDTO::getPermission) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); - } - } - // 设置已提示的 key - user.setTippedKeys(tipsService.getTippedKeys()); - // 获取异步结果 - user.setSystemPreference(systemPreference.get()); + // 获取角色权限 + List permissions = this.getRolePermissions(roles); + // 提示信息 + List tippedKeys = tipsService.getTippedKeys(); // 组装数据 return UserPermissionVO.builder() - .user(user) + .user(SystemUserConvert.MAPPER.toBase(user)) .roles(roles.values()) .permissions(permissions) + .systemPreference(systemPreference.get()) + .tippedKeys(tippedKeys) .build(); } @@ -317,4 +302,31 @@ public class UserPermissionServiceImpl implements UserPermissionService { return roles; } + /** + * 获取角色对应的权限 + * + * @param roles roles + * @return 权限 + */ + private List getRolePermissions(Map roles) { + if (Maps.isEmpty(roles)) { + return Lists.empty(); + } + // 管理员拥有全部权限 + if (RoleDefine.containsAdmin(roles.values())) { + return Lists.singleton(Const.ASTERISK); + } + // 角色权限 + return roles.keySet() + .stream() + .map(roleMenuCache::get) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .filter(s -> MenuStatusEnum.ENABLED.getStatus().equals(s.getStatus())) + .map(SystemMenuCacheDTO::getPermission) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + } + } diff --git a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/resources/mapper/SystemUserMapper.xml b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/resources/mapper/SystemUserMapper.xml index 3d3ff1c9..8320530a 100644 --- a/orion-visor-module-infra/orion-visor-module-infra-service/src/main/resources/mapper/SystemUserMapper.xml +++ b/orion-visor-module-infra/orion-visor-module-infra-service/src/main/resources/mapper/SystemUserMapper.xml @@ -17,12 +17,14 @@ + + - id, username, password, nickname, avatar, mobile, email, status, last_login_time, create_time, update_time, creator, updater, deleted + id, username, password, nickname, avatar, mobile, email, status, update_password_status, update_password_reason, last_login_time, create_time, update_time, creator, updater, deleted diff --git a/orion-visor-ui/src/api/user/auth.ts b/orion-visor-ui/src/api/user/auth.ts index 0c50310f..08968a0b 100644 --- a/orion-visor-ui/src/api/user/auth.ts +++ b/orion-visor-ui/src/api/user/auth.ts @@ -1,4 +1,3 @@ -import type { MenuQueryResponse } from '@/api/system/menu'; import axios from 'axios'; /** @@ -17,45 +16,15 @@ export interface LoginResponse { } /** - * 用户权限响应 + * 用户登录 */ -export interface UserPermissionResponse { - user: { - id: number; - username: string; - nickname: string; - avatar: string; - systemPreference: Record; - tippedKeys: Array; - }; - roles: Array; - permissions: Array; -} - -/** - * 登录 - */ -export function login(data: LoginRequest) { +export function userLogin(data: LoginRequest) { return axios.post('/infra/auth/login', data); } /** - * 登出 + * 用户登出 */ -export function logout() { +export function userLogout() { return axios.get('/infra/auth/logout'); } - -/** - * 获取用户信息 - */ -export function getUserPermission() { - return axios.get('/infra/user-permission/user'); -} - -/** - * 获取菜单列表 - */ -export function getMenuList() { - return axios.get>('/infra/user-permission/menu'); -} diff --git a/orion-visor-ui/src/api/user/mine.ts b/orion-visor-ui/src/api/user/mine.ts index b768ba55..3be27586 100644 --- a/orion-visor-ui/src/api/user/mine.ts +++ b/orion-visor-ui/src/api/user/mine.ts @@ -9,6 +9,7 @@ import axios from 'axios'; export interface UserUpdatePasswordRequest { beforePassword?: string; password?: string; + checkPassword?: string; } /** diff --git a/orion-visor-ui/src/api/user/permission.ts b/orion-visor-ui/src/api/user/permission.ts new file mode 100644 index 00000000..344bf7f4 --- /dev/null +++ b/orion-visor-ui/src/api/user/permission.ts @@ -0,0 +1,39 @@ +import type { MenuQueryResponse } from '@/api/system/menu'; +import axios from 'axios'; + +/** + * 用户权限响应 + */ +export interface UserPermissionResponse { + user: UserBaseResponse; + roles: Array; + permissions: Array; + systemPreference: Record; + tippedKeys: Array; +} + +/** + * 用户基础信息 + */ +export interface UserBaseResponse { + id: number; + username: string; + nickname: string; + avatar: string; + passwordUpdateStatus: number; + passwordUpdateReason: string; +} + +/** + * 获取用户信息 + */ +export function getUserPermission() { + return axios.get('/infra/user-permission/user'); +} + +/** + * 获取菜单列表 + */ +export function getUserMenuList() { + return axios.get>('/infra/user-permission/menu'); +} diff --git a/orion-visor-ui/src/components/app/tab-bar/tab-item.vue b/orion-visor-ui/src/components/app/tab-bar/tab-item.vue index 660fac4b..58ecf2b4 100644 --- a/orion-visor-ui/src/components/app/tab-bar/tab-item.vue +++ b/orion-visor-ui/src/components/app/tab-bar/tab-item.vue @@ -122,7 +122,6 @@ // 关闭左侧 const currentRouteIdx = findCurrentRouteIndex(); copyTagList.splice(1, props.index - 1); - tabBarStore.freshTabList(copyTagList); if (currentRouteIdx < index) { await router.push({ name: itemData.name }); @@ -131,7 +130,6 @@ // 关闭右侧 const currentRouteIdx = findCurrentRouteIndex(); copyTagList.splice(props.index + 1); - tabBarStore.freshTabList(copyTagList); if (currentRouteIdx > index) { await router.push({ name: itemData.name }); @@ -154,7 +152,8 @@ } else { // 关闭全部 tabBarStore.resetTabList(); - await router.push({ name: DEFAULT_ROUTE_NAME }); + // 跳转到首页 添加 query 强行刷新 + await router.push({ name: DEFAULT_ROUTE_NAME, query: { _: Date.now() } }); } }; diff --git a/orion-visor-ui/src/components/user/user/update-password-modal/index.vue b/orion-visor-ui/src/components/user/user/update-password-modal/index.vue index 28ef7346..20adea9b 100644 --- a/orion-visor-ui/src/components/user/user/update-password-modal/index.vue +++ b/orion-visor-ui/src/components/user/user/update-password-modal/index.vue @@ -2,7 +2,7 @@ - - + :rules="rules"> + + - - + + + + + + @@ -38,12 +47,13 @@ + + - + + diff --git a/orion-visor-ui/src/views/base/status/not-found/index.vue b/orion-visor-ui/src/views/base/status/not-found/index.vue index f0bc80d0..75639cf5 100644 --- a/orion-visor-ui/src/views/base/status/not-found/index.vue +++ b/orion-visor-ui/src/views/base/status/not-found/index.vue @@ -1,44 +1,34 @@ - - + + diff --git a/orion-visor-ui/src/views/base/update-password/index.vue b/orion-visor-ui/src/views/base/update-password/index.vue new file mode 100644 index 00000000..eef852d1 --- /dev/null +++ b/orion-visor-ui/src/views/base/update-password/index.vue @@ -0,0 +1,165 @@ + + + + + + + diff --git a/orion-visor-ui/src/views/base/update-password/types/const.ts b/orion-visor-ui/src/views/base/update-password/types/const.ts new file mode 100644 index 00000000..f30c46a4 --- /dev/null +++ b/orion-visor-ui/src/views/base/update-password/types/const.ts @@ -0,0 +1,5 @@ +// 修改密码原因 +export const updatePasswordReasonKey = 'updatePasswordReason'; + +// 加载的字典值 +export const dictKeys = [updatePasswordReasonKey]; diff --git a/sql/init-4-data.sql b/sql/init-4-data.sql index f5682603..818da32f 100644 --- a/sql/init-4-data.sql +++ b/sql/init-4-data.sql @@ -1,6 +1,6 @@ -- 默认管理员账号 -- 账号: admin 密码: admin -INSERT INTO `system_user` VALUES (1, 'admin', 'c3284d0f94606de1fd2af172aba15bf3', '管理员', '', '', '', 1, '2023-08-17 14:23:59', '2023-07-13 22:11:57', '2023-08-17 14:23:59', '1', '1', 0); +INSERT INTO `system_user` VALUES (1, 'admin', 'c3284d0f94606de1fd2af172aba15bf3', '管理员', NULL, NULL, NULL, 1, 1, 'INIT', NULL, NOW(), NOW(), '1', '1', 0); -- 角色配置 INSERT INTO `system_role` VALUES (1, '管理员', 'admin', 1, '2023-07-16 21:13:14', '2023-07-17 17:31:29', '1', '1', 0); diff --git a/sql/常用.sql b/sql/常用.sql index 715d3c22..b30d1476 100644 --- a/sql/常用.sql +++ b/sql/常用.sql @@ -9,10 +9,9 @@ CREATE TABLE `table` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='table'; - -- 添加默认列 ALTER TABLE `table` -ADD COLUMN `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' AFTER type, +ADD COLUMN `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间' AFTER id, ADD COLUMN `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间' AFTER create_time, ADD COLUMN `creator` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '创建人' AFTER update_time, ADD COLUMN `updater` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '更新人' AFTER creator,