From 9dc5b1d9eef12f00db5c2193437bb14c3c3dffef Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 2 Jan 2026 14:29:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E5=8D=95=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E5=A4=9A=E9=83=A8=E9=97=A8=E6=94=AF=E6=8C=81=EF=BC=9A=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E4=B8=AD=E9=97=B4=E8=A1=A8=E3=80=81=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E7=B1=BB=E3=80=81Mapper=E5=92=8C=E5=A4=9A=E9=83=A8=E9=97=A8?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=96=B9=E6=B3=95=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E9=92=89=E9=92=89=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8C=B9=E9=85=8D=E5=A4=9A=E4=B8=AA=E9=83=A8=E9=97=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/designhub/entity/UserDepartment.java | 49 +++ .../mapper/UserDepartmentMapper.java | 12 + .../com/designhub/service/UserService.java | 393 ++++++++++++++---- .../com/designhub/vo/UserDepartmentVO.java | 26 ++ .../java/com/designhub/vo/UserInfoVO.java | 9 +- database/add_user_department_table.sql | 25 ++ deploy_from_git.sh | 89 ++++ 服务器部署命令.txt | 35 ++ 8 files changed, 561 insertions(+), 77 deletions(-) create mode 100644 backend/src/main/java/com/designhub/entity/UserDepartment.java create mode 100644 backend/src/main/java/com/designhub/mapper/UserDepartmentMapper.java create mode 100644 backend/src/main/java/com/designhub/vo/UserDepartmentVO.java create mode 100644 database/add_user_department_table.sql create mode 100755 deploy_from_git.sh create mode 100644 服务器部署命令.txt diff --git a/backend/src/main/java/com/designhub/entity/UserDepartment.java b/backend/src/main/java/com/designhub/entity/UserDepartment.java new file mode 100644 index 0000000..0a6128b --- /dev/null +++ b/backend/src/main/java/com/designhub/entity/UserDepartment.java @@ -0,0 +1,49 @@ +package com.designhub.entity; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 用户部门关联实体类 + */ +@Data +@TableName("sys_user_department") +public class UserDepartment implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(value = "id", type = IdType.AUTO) + private Long id; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 是否为主部门:0-否,1-是 + */ + private Integer isPrimary; + + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + private LocalDateTime updateTime; +} diff --git a/backend/src/main/java/com/designhub/mapper/UserDepartmentMapper.java b/backend/src/main/java/com/designhub/mapper/UserDepartmentMapper.java new file mode 100644 index 0000000..6960f66 --- /dev/null +++ b/backend/src/main/java/com/designhub/mapper/UserDepartmentMapper.java @@ -0,0 +1,12 @@ +package com.designhub.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.designhub.entity.UserDepartment; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户部门关联Mapper接口 + */ +@Mapper +public interface UserDepartmentMapper extends BaseMapper { +} diff --git a/backend/src/main/java/com/designhub/service/UserService.java b/backend/src/main/java/com/designhub/service/UserService.java index 7be01e1..8944465 100644 --- a/backend/src/main/java/com/designhub/service/UserService.java +++ b/backend/src/main/java/com/designhub/service/UserService.java @@ -7,13 +7,16 @@ import com.designhub.dto.LoginDTO; import com.designhub.dto.RegisterDTO; import com.designhub.entity.Department; import com.designhub.entity.User; +import com.designhub.entity.UserDepartment; import com.designhub.exception.BusinessException; import com.designhub.mapper.UserMapper; import com.designhub.mapper.DepartmentMapper; +import com.designhub.mapper.UserDepartmentMapper; import com.designhub.common.ResultCode; import com.designhub.util.JwtUtil; import com.designhub.vo.LoginVO; import com.designhub.vo.UserInfoVO; +import com.designhub.vo.UserDepartmentVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; @@ -25,6 +28,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.HashMap; import java.util.stream.Collectors; /** @@ -37,6 +41,7 @@ public class UserService { private final UserMapper userMapper; private final DepartmentMapper departmentMapper; + private final UserDepartmentMapper userDepartmentMapper; private final TeamScorePoolService teamScorePoolService; private final RoleService roleService; private final DingTalkService dingTalkService; @@ -487,73 +492,52 @@ public class UserService { needUpdate = true; } - // 3. 检查部门是否需要更新 + // 3. 同步部门(支持多部门) if (dingDepartments != null && !dingDepartments.isEmpty()) { - if (user.getDeptId() != null) { - // 用户已有部门:检查是否需要更新 - Department currentDept = departmentMapper.selectById(user.getDeptId()); - if (currentDept != null && StringUtils.hasText(currentDept.getDingDeptId())) { - // 检查用户是否还在原部门 - boolean stillInDept = dingDepartments.stream() - .anyMatch(d -> currentDept.getDingDeptId().equals(d.get("dept_id").toString())); - - if (!stillInDept) { - String newDeptNames = dingDepartments.stream() - .map(d -> String.valueOf(d.get("name"))) - .collect(Collectors.joining(",")); - log.warn("用户钉钉部门已变化: userId={}, 原部门={}, 钉钉部门={}", - user.getId(), currentDept.getDeptName(), newDeptNames); - - // 尝试重新匹配部门 - Long newDeptId = matchSystemDepartment(dingDepartments); - if (newDeptId != null && !newDeptId.equals(user.getDeptId())) { - Department newDept = departmentMapper.selectById(newDeptId); - user.setDeptId(newDeptId); - user.setDeptName(newDept.getDeptName()); - // 根据部门类型设置角色 - user.setRoleIds("receiving".equals(newDept.getDeptType()) ? "5" : "6"); - user.setStatus(1); // 启用用户 - needUpdate = true; - log.info("自动同步到新部门: userId={}, 新部门={}", - user.getId(), newDept.getDeptName()); - } - } - } - } else { - // 用户没有部门:尝试匹配部门 - Long matchedDeptId = matchSystemDepartment(dingDepartments); - if (matchedDeptId != null) { - Department dept = departmentMapper.selectById(matchedDeptId); - user.setDeptId(matchedDeptId); - user.setDeptName(dept.getDeptName()); - user.setRoleIds("receiving".equals(dept.getDeptType()) ? "5" : "6"); + // 匹配多个部门 + List matchedDeptIds = matchSystemDepartments(dingDepartments); + + if (!matchedDeptIds.isEmpty()) { + // 同步多个部门到sys_user_department表 + // 第一个匹配的部门作为主部门 + Long primaryDeptId = matchedDeptIds.get(0); + setUserDepartments(user.getId(), matchedDeptIds, primaryDeptId); + + // 更新sys_user表的主部门(向后兼容) + Department primaryDept = departmentMapper.selectById(primaryDeptId); + if (primaryDept != null) { + user.setDeptId(primaryDeptId); + user.setDeptName(primaryDept.getDeptName()); + // 根据主部门类型设置角色 + user.setRoleIds("receiving".equals(primaryDept.getDeptType()) ? "5" : "6"); user.setStatus(1); // 启用用户 needUpdate = true; - log.info("已有用户匹配到部门: userId={}, deptId={}, deptName={}", - user.getId(), matchedDeptId, dept.getDeptName()); - } else { - // 无法匹配,使用第一个可用部门作为默认值 - List availableDepts = departmentMapper.selectList( - new LambdaQueryWrapper() - .eq(Department::getStatus, 1) - .eq(Department::getDeleted, 0) - .orderByAsc(Department::getSortOrder) - .orderByAsc(Department::getId) - ); - - if (!availableDepts.isEmpty()) { - Department defaultDept = availableDepts.get(0); - user.setDeptId(defaultDept.getId()); - user.setDeptName(defaultDept.getDeptName()); - user.setRoleIds("receiving".equals(defaultDept.getDeptType()) ? "5" : "6"); - user.setStatus(1); // 启用用户 - needUpdate = true; - log.warn("已有用户无法匹配,使用默认部门: userId={}, deptId={}, deptName={}, 钉钉部门: {}", - user.getId(), defaultDept.getId(), defaultDept.getDeptName(), - dingDepartments.stream() - .map(d -> String.valueOf(d.get("name"))) - .collect(Collectors.joining(", "))); - } + log.info("同步用户多个部门: userId={}, 部门数量={}, 主部门={}", + user.getId(), matchedDeptIds.size(), primaryDept.getDeptName()); + } + } else { + // 无法匹配,使用第一个可用部门作为默认值 + List availableDepts = departmentMapper.selectList( + new LambdaQueryWrapper() + .eq(Department::getStatus, 1) + .eq(Department::getDeleted, 0) + .orderByAsc(Department::getSortOrder) + .orderByAsc(Department::getId) + ); + + if (!availableDepts.isEmpty()) { + Department defaultDept = availableDepts.get(0); + setUserDepartments(user.getId(), List.of(defaultDept.getId()), defaultDept.getId()); + user.setDeptId(defaultDept.getId()); + user.setDeptName(defaultDept.getDeptName()); + user.setRoleIds("receiving".equals(defaultDept.getDeptType()) ? "5" : "6"); + user.setStatus(1); // 启用用户 + needUpdate = true; + log.warn("已有用户无法匹配,使用默认部门: userId={}, deptId={}, deptName={}, 钉钉部门: {}", + user.getId(), defaultDept.getId(), defaultDept.getDeptName(), + dingDepartments.stream() + .map(d -> String.valueOf(d.get("name"))) + .collect(Collectors.joining(", "))); } } } @@ -624,8 +608,8 @@ public class UserService { // 需要用户手动选择部门类型和具体部门 log.info("钉钉用户首次登录,创建临时账号: email={}", email); - // 尝试匹配系统部门 - Long matchedDeptId = matchSystemDepartment(dingDepartments); + // 尝试匹配系统部门(支持多部门) + List matchedDeptIds = matchSystemDepartments(dingDepartments); String username = detailedInfo != null && detailedInfo.get("job_number") != null ? (String) detailedInfo.get("job_number") : "DT_" + unionId.substring(0, 8); @@ -642,15 +626,16 @@ public class UserService { user.setStatus(1); user.setDeleted(0); - if (matchedDeptId != null) { - // 自动匹配到部门 - Department dept = departmentMapper.selectById(matchedDeptId); - user.setDeptId(matchedDeptId); - user.setDeptName(dept.getDeptName()); - // 根据部门类型设置默认角色 - user.setRoleIds("receiving".equals(dept.getDeptType()) ? "5" : "6"); - log.info("自动匹配到系统部门: deptId={}, deptName={}, type={}", - matchedDeptId, dept.getDeptName(), dept.getDeptType()); + if (!matchedDeptIds.isEmpty()) { + // 自动匹配到多个部门,第一个作为主部门 + Long primaryDeptId = matchedDeptIds.get(0); + Department primaryDept = departmentMapper.selectById(primaryDeptId); + user.setDeptId(primaryDeptId); + user.setDeptName(primaryDept.getDeptName()); + // 根据主部门类型设置默认角色 + user.setRoleIds("receiving".equals(primaryDept.getDeptType()) ? "5" : "6"); + log.info("自动匹配到系统部门(多部门): userId={}, 部门数量={}, 主部门={}", + username, matchedDeptIds.size(), primaryDept.getDeptName()); } else { // 无法自动匹配,使用第一个可用部门作为默认值 List availableDepts = departmentMapper.selectList( @@ -667,6 +652,7 @@ public class UserService { user.setDeptId(defaultDept.getId()); user.setDeptName(defaultDept.getDeptName()); user.setRoleIds("receiving".equals(defaultDept.getDeptType()) ? "5" : "6"); + matchedDeptIds = List.of(defaultDept.getId()); log.warn("无法自动匹配系统部门,使用默认部门: deptId={}, deptName={}, 钉钉部门: {}", defaultDept.getId(), defaultDept.getDeptName(), dingDepartments.stream() @@ -686,6 +672,12 @@ public class UserService { } userMapper.insert(user); + + // 同步多个部门到sys_user_department表 + if (!matchedDeptIds.isEmpty()) { + Long primaryDeptId = matchedDeptIds.get(0); + setUserDepartments(user.getId(), matchedDeptIds, primaryDeptId); + } log.info("钉钉用户创建成功: userId={}, username={}, needSelectDept={}", user.getId(), user.getUsername(), matchedDeptId == null); @@ -861,6 +853,43 @@ public class UserService { log.warn("检查部门负责人状态失败: userId={}, error={}", user.getId(), e.getMessage()); } + // 获取用户的所有部门列表 + List departments = List.of(); + try { + List userDepts = userDepartmentMapper.selectList( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, user.getId()) + ); + + if (!userDepts.isEmpty()) { + List deptIds = userDepts.stream() + .map(UserDepartment::getDeptId) + .collect(Collectors.toList()); + List depts = departmentMapper.selectBatchIds(deptIds); + + // 构建部门VO列表,保持isPrimary顺序 + Map deptMap = depts.stream() + .collect(Collectors.toMap(Department::getId, d -> d)); + + departments = userDepts.stream() + .map(ud -> { + Department dept = deptMap.get(ud.getDeptId()); + if (dept != null) { + return UserDepartmentVO.builder() + .deptId(dept.getId()) + .deptName(dept.getDeptName()) + .isPrimary(ud.getIsPrimary()) + .build(); + } + return null; + }) + .filter(d -> d != null) + .collect(Collectors.toList()); + } + } catch (Exception e) { + log.warn("获取用户部门列表失败: userId={}, error={}", user.getId(), e.getMessage()); + } + UserInfoVO userInfoVO = UserInfoVO.builder() .id(user.getId()) .username(user.getUsername()) @@ -870,6 +899,7 @@ public class UserService { .avatar(user.getAvatar()) .deptId(user.getDeptId()) .deptName(user.getDeptName()) + .departments(departments) .roleIds(user.getRoleIds()) .roles(roleCodes) .status(user.getStatus()) @@ -918,4 +948,217 @@ public class UserService { .roleIds(user.getRoleIds()) .build(); } + + // ==================== 多部门支持方法 ==================== + + /** + * 获取用户的所有部门ID列表 + */ + public List getUserDepartmentIds(Long userId) { + List userDepts = userDepartmentMapper.selectList( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + ); + return userDepts.stream() + .map(UserDepartment::getDeptId) + .collect(Collectors.toList()); + } + + /** + * 获取用户的所有部门信息 + */ + public List getUserDepartments(Long userId) { + List deptIds = getUserDepartmentIds(userId); + if (deptIds.isEmpty()) { + return List.of(); + } + return departmentMapper.selectBatchIds(deptIds); + } + + /** + * 添加用户到部门(如果不存在则添加) + */ + @Transactional(rollbackFor = Exception.class) + public void addUserToDepartment(Long userId, Long deptId, boolean isPrimary) { + // 检查是否已存在 + UserDepartment exist = userDepartmentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + .eq(UserDepartment::getDeptId, deptId) + ); + + if (exist == null) { + UserDepartment userDept = new UserDepartment(); + userDept.setUserId(userId); + userDept.setDeptId(deptId); + userDept.setIsPrimary(isPrimary ? 1 : 0); + userDepartmentMapper.insert(userDept); + log.info("添加用户到部门: userId={}, deptId={}, isPrimary={}", userId, deptId, isPrimary); + } else if (isPrimary && exist.getIsPrimary() == 0) { + // 更新为主部门 + exist.setIsPrimary(1); + userDepartmentMapper.updateById(exist); + log.info("设置为主部门: userId={}, deptId={}", userId, deptId); + } + + // 如果设置为主部门,需要更新sys_user表的dept_id + if (isPrimary) { + Department dept = departmentMapper.selectById(deptId); + if (dept != null) { + User user = userMapper.selectById(userId); + if (user != null) { + user.setDeptId(deptId); + user.setDeptName(dept.getDeptName()); + userMapper.updateById(user); + } + } + + // 将其他部门设置为非主部门 + userDepartmentMapper.update(null, + new LambdaUpdateWrapper() + .eq(UserDepartment::getUserId, userId) + .ne(UserDepartment::getDeptId, deptId) + .set(UserDepartment::getIsPrimary, 0) + ); + } + } + + /** + * 从部门中移除用户 + */ + @Transactional(rollbackFor = Exception.class) + public void removeUserFromDepartment(Long userId, Long deptId) { + // 检查是否为主部门 + UserDepartment userDept = userDepartmentMapper.selectOne( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + .eq(UserDepartment::getDeptId, deptId) + ); + + if (userDept != null && userDept.getIsPrimary() == 1) { + // 如果是主部门,需要设置另一个部门为主部门,或清空主部门 + List otherDepts = userDepartmentMapper.selectList( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + .ne(UserDepartment::getDeptId, deptId) + .last("LIMIT 1") + ); + + if (!otherDepts.isEmpty()) { + // 设置第一个其他部门为主部门 + UserDepartment newPrimary = otherDepts.get(0); + newPrimary.setIsPrimary(1); + userDepartmentMapper.updateById(newPrimary); + + Department newDept = departmentMapper.selectById(newPrimary.getDeptId()); + if (newDept != null) { + User user = userMapper.selectById(userId); + if (user != null) { + user.setDeptId(newDept.getId()); + user.setDeptName(newDept.getDeptName()); + userMapper.updateById(user); + } + } + } else { + // 没有其他部门,清空主部门 + User user = userMapper.selectById(userId); + if (user != null) { + user.setDeptId(null); + user.setDeptName(null); + userMapper.updateById(user); + } + } + } + + // 删除关联 + userDepartmentMapper.delete( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + .eq(UserDepartment::getDeptId, deptId) + ); + log.info("从部门移除用户: userId={}, deptId={}", userId, deptId); + } + + /** + * 设置用户的部门列表(替换现有部门) + */ + @Transactional(rollbackFor = Exception.class) + public void setUserDepartments(Long userId, List deptIds, Long primaryDeptId) { + // 删除所有现有关联 + userDepartmentMapper.delete( + new LambdaQueryWrapper() + .eq(UserDepartment::getUserId, userId) + ); + + // 添加新关联 + if (deptIds != null && !deptIds.isEmpty()) { + for (Long deptId : deptIds) { + boolean isPrimary = deptId.equals(primaryDeptId); + addUserToDepartment(userId, deptId, isPrimary); + } + } else { + // 清空部门 + User user = userMapper.selectById(userId); + if (user != null) { + user.setDeptId(null); + user.setDeptName(null); + userMapper.updateById(user); + } + } + } + + /** + * 匹配多个系统部门(根据钉钉部门列表) + */ + private List matchSystemDepartments(List> dingDepartments) { + if (dingDepartments == null || dingDepartments.isEmpty()) { + return List.of(); + } + + // 获取所有系统部门 + List systemDepts = departmentMapper.selectList( + new LambdaQueryWrapper() + .eq(Department::getStatus, 1) + .eq(Department::getDeleted, 0) + ); + + List matchedDeptIds = new ArrayList<>(); + + for (Map dingDept : dingDepartments) { + String dingDeptName = (String) dingDept.get("name"); + Object dingDeptIdObj = dingDept.get("dept_id"); + String dingDeptId = dingDeptIdObj != null ? dingDeptIdObj.toString() : null; + + Department matched = null; + + // 1. 优先通过钉钉部门ID精确匹配 + if (dingDeptId != null) { + matched = systemDepts.stream() + .filter(d -> dingDeptId.equals(d.getDingDeptId())) + .findFirst() + .orElse(null); + if (matched != null) { + log.info("通过钉钉部门ID精确匹配: {} -> {}", dingDeptId, matched.getDeptName()); + } + } + + // 2. 通过部门名称模糊匹配 + if (matched == null && dingDeptName != null) { + matched = systemDepts.stream() + .filter(d -> d.getDeptName().contains(dingDeptName) || + dingDeptName.contains(d.getDeptName())) + .findFirst() + .orElse(null); + if (matched != null) { + log.info("通过部门名称模糊匹配: {} -> {}", dingDeptName, matched.getDeptName()); + } + } + + if (matched != null && !matchedDeptIds.contains(matched.getId())) { + matchedDeptIds.add(matched.getId()); + } + } + + return matchedDeptIds; + } } diff --git a/backend/src/main/java/com/designhub/vo/UserDepartmentVO.java b/backend/src/main/java/com/designhub/vo/UserDepartmentVO.java new file mode 100644 index 0000000..0752c26 --- /dev/null +++ b/backend/src/main/java/com/designhub/vo/UserDepartmentVO.java @@ -0,0 +1,26 @@ +package com.designhub.vo; + +import lombok.Data; +import lombok.Builder; + +/** + * 用户部门信息VO + */ +@Data +@Builder +public class UserDepartmentVO { + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 是否为主部门:0-否,1-是 + */ + private Integer isPrimary; +} diff --git a/backend/src/main/java/com/designhub/vo/UserInfoVO.java b/backend/src/main/java/com/designhub/vo/UserInfoVO.java index a6767b3..1b5efff 100644 --- a/backend/src/main/java/com/designhub/vo/UserInfoVO.java +++ b/backend/src/main/java/com/designhub/vo/UserInfoVO.java @@ -42,14 +42,19 @@ public class UserInfoVO { private String avatar; /** - * 部门ID + * 部门ID(主部门,向后兼容) */ private Long deptId; /** - * 部门名称 + * 部门名称(主部门,向后兼容) */ private String deptName; + + /** + * 用户的所有部门列表(多部门支持) + */ + private List departments; /** * 角色ID列表(逗号分隔) diff --git a/database/add_user_department_table.sql b/database/add_user_department_table.sql new file mode 100644 index 0000000..7cdd17f --- /dev/null +++ b/database/add_user_department_table.sql @@ -0,0 +1,25 @@ +-- 用户部门关联表(支持多部门) +-- 创建时间: 2026-01-02 +-- 说明: 支持单用户属于多个部门 + +CREATE TABLE IF NOT EXISTS `sys_user_department` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `dept_id` BIGINT NOT NULL COMMENT '部门ID', + `is_primary` TINYINT DEFAULT 0 COMMENT '是否为主部门:0-否,1-是', + `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_user_dept` (`user_id`, `dept_id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_dept_id` (`dept_id`), + CONSTRAINT `fk_user_dept_user` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk_user_dept_dept` FOREIGN KEY (`dept_id`) REFERENCES `sys_dept` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户部门关联表'; + +-- 数据迁移:将现有sys_user表中的dept_id迁移到sys_user_department表 +INSERT INTO `sys_user_department` (`user_id`, `dept_id`, `is_primary`) +SELECT `id`, `dept_id`, 1 +FROM `sys_user` +WHERE `dept_id` IS NOT NULL AND `dept_id` > 0 +ON DUPLICATE KEY UPDATE `is_primary` = 1; diff --git a/deploy_from_git.sh b/deploy_from_git.sh new file mode 100755 index 0000000..a80b7de --- /dev/null +++ b/deploy_from_git.sh @@ -0,0 +1,89 @@ +#!/bin/bash +############################################################################### +# 从Git拉取代码并部署到服务器 +# 服务器: 45.192.99.48 +############################################################################### + +SERVER_IP="45.192.99.48" +SERVER_USER="root" +SERVER_PASSWORD="1blZllMIp7" +APP_DIR="/opt/designhub" +GIT_REPO="http://chenpei:jsjj654321@git.1080p.hk/chenpei/qiangdanxitong.git" + +# 颜色定义 +GREEN='\033[0;32m' +BLUE='\033[0;34m' +RED='\033[0;31m' +NC='\033[0m' + +echo "========================================" +echo -e "${GREEN}从Git拉取代码并部署${NC}" +echo "========================================" +echo "" +echo "服务器: ${SERVER_IP}" +echo "Git仓库: ${GIT_REPO}" +echo "" + +# 检查sshpass +if ! command -v sshpass &> /dev/null; then + echo "正在安装sshpass..." + if [[ "$OSTYPE" == "darwin"* ]]; then + brew install hudochenkov/sshpass/sshpass 2>/dev/null || { + echo -e "${RED}请先安装sshpass: brew install hudochenkov/sshpass/sshpass${NC}" + exit 1 + } + fi +fi + +# 在服务器上执行部署 +sshpass -p "${SERVER_PASSWORD}" ssh -o StrictHostKeyChecking=no ${SERVER_USER}@${SERVER_IP} << ENDSSH +set -e + +echo -e "${BLUE}[1/6] 检查Git仓库...${NC}" +cd ${APP_DIR} + +if [ ! -d ".git" ]; then + echo "初始化Git仓库..." + # 先暂存现有文件 + git init + git remote add origin ${GIT_REPO} 2>/dev/null || git remote set-url origin ${GIT_REPO} + # 添加现有文件到Git + git add -A 2>/dev/null || true + git commit -m "Backup existing files" 2>/dev/null || true + # 拉取最新代码(允许合并) + git pull origin main --allow-unrelated-histories || git fetch origin main:main && git checkout main +else + echo "拉取最新代码..." + git fetch origin + git reset --hard origin/main +fi + +echo -e "${BLUE}[2/6] 备份当前JAR文件...${NC}" +if [ -f "${APP_DIR}/designhub.jar" ]; then + cp ${APP_DIR}/designhub.jar ${APP_DIR}/designhub.jar.backup.\$(date +%Y%m%d_%H%M%S) || true +fi + +echo -e "${BLUE}[3/6] 构建后端...${NC}" +cd ${APP_DIR}/backend +mvn clean package -DskipTests + +echo -e "${BLUE}[4/6] 复制JAR文件...${NC}" +cp target/designhub-backend-1.0.0.jar ${APP_DIR}/designhub.jar + +echo -e "${BLUE}[5/6] 重启服务...${NC}" +systemctl restart designhub + +echo "" +echo -e "${GREEN}等待服务启动...${NC}" +sleep 5 + +echo "" +echo -e "${BLUE}[6/6] 检查服务状态...${NC}" +systemctl status designhub --no-pager | head -15 + +echo "" +echo -e "${GREEN}✅ 部署完成!${NC}" +ENDSSH + +echo "" +echo -e "${GREEN}✅ 部署脚本执行完成!${NC}" diff --git a/服务器部署命令.txt b/服务器部署命令.txt new file mode 100644 index 0000000..28fdbbe --- /dev/null +++ b/服务器部署命令.txt @@ -0,0 +1,35 @@ +# 在服务器上执行以下命令部署最新代码 + +# 1. 连接到服务器 +ssh root@45.192.99.48 +# 密码: 1blZllMIp7 + +# 2. 进入项目目录 +cd /opt/designhub + +# 3. 如果还没有Git仓库,先初始化 +if [ ! -d ".git" ]; then + git init + git remote add origin http://chenpei:jsjj654321@git.1080p.hk/chenpei/qiangdanxitong.git + git fetch origin + git checkout -b main origin/main +else + # 拉取最新代码 + git pull origin main +fi + +# 4. 构建后端 +cd backend +mvn clean package -DskipTests + +# 5. 复制JAR文件 +cp target/designhub-backend-1.0.0.jar /opt/designhub/designhub.jar + +# 6. 重启服务 +systemctl restart designhub + +# 7. 检查服务状态 +systemctl status designhub + +# 8. 查看日志(可选) +tail -f /opt/designhub/logs/application.log