diff --git a/pom.xml b/pom.xml index 142869ff97..8659a524d2 100644 --- a/pom.xml +++ b/pom.xml @@ -15,13 +15,13 @@ yudao-module-system yudao-module-infra - - + yudao-module-member + yudao-module-bpm - + yudao-module-crm diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/invite/InviteUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/invite/InviteUtils.java new file mode 100644 index 0000000000..a5d04b0a87 --- /dev/null +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/invite/InviteUtils.java @@ -0,0 +1,118 @@ +package cn.iocoder.yudao.framework.common.util.invite; +import java.util.Arrays; + +// 邀请码工具类 +// 主要用于生成邀请码,以及校验邀请码是否正确 +// 邀请码的生成规则是: +// 1. 邀请码的长度为 6 位 +public class InviteUtils { + /** + * 随机字符串 + */ + private static final char[] CHARS = new char[] { 'F', 'L', 'G', 'W', '5', 'X', 'C', '3', '9', 'Z', 'M', '6', '7', + 'Y', 'R', 'T', '2', 'H', 'S', '8', 'D', 'V', 'E', 'J', '4', 'K', 'Q', 'P', 'U', 'A', 'N', 'B' }; + + private final static int CHARS_LENGTH = 32; + /** + * 邀请码长度 + */ + private final static int CODE_LENGTH = 6; + + /** + * 随机数据 + */ + private final static long SLAT = 1234561L; + + /** + * PRIME1 与 CHARS 的长度 L互质,可保证 ( id * PRIME1) % L 在 [0,L)上均匀分布 + */ + private final static int PRIME1 = 3; + + /** + * PRIME2 与 CODE_LENGTH 互质,可保证 ( index * PRIME2) % CODE_LENGTH 在 + * [0,CODE_LENGTH)上均匀分布 + */ + private final static int PRIME2 = 11; + + /** + * 生成邀请码 + * + * @param id 唯一的id主键 + * @return code + */ + public static String generateInviteCode(Long id) { + // 补位 + id = id * PRIME1 + SLAT; + // 将 id 转换成32进制的值 + long[] b = new long[CODE_LENGTH]; + // 32进制数 + b[0] = id; + for (int i = 0; i < CODE_LENGTH - 1; i++) { + b[i + 1] = b[i] / CHARS_LENGTH; + // 按位扩散 + b[i] = (b[i] + i * b[0]) % CHARS_LENGTH; + } + b[5] = (b[0] + b[1] + b[2] + b[3] + b[4]) * PRIME1 % CHARS_LENGTH; + + // 进行混淆 + long[] codeIndexArray = new long[CODE_LENGTH]; + for (int i = 0; i < CODE_LENGTH; i++) { + codeIndexArray[i] = b[i * PRIME2 % CODE_LENGTH]; + } + + StringBuilder buffer = new StringBuilder(); + Arrays.stream(codeIndexArray).boxed().map(Long::intValue).map(t -> CHARS[t]).forEach(buffer::append); + return buffer.toString(); + } + + /** + * 将邀请码解密成原来的id + * + * @param code 邀请码 + * @return id + */ + public static Long deInviteCode(String code) { + if (code.length() != CODE_LENGTH) { + return null; + } + // 将字符还原成对应数字 + long[] a = new long[CODE_LENGTH]; + for (int i = 0; i < CODE_LENGTH; i++) { + char c = code.charAt(i); + int index = findIndex(c); + if (index == -1) { + // 异常字符串 + return null; + } + a[i * PRIME2 % CODE_LENGTH] = index; + } + + long[] b = new long[CODE_LENGTH]; + for (int i = CODE_LENGTH - 2; i >= 0; i--) { + b[i] = (a[i] - a[0] * i + CHARS_LENGTH * i) % CHARS_LENGTH; + } + + long res = 0; + for (int i = CODE_LENGTH - 2; i >= 0; i--) { + res += b[i]; + res *= (i > 0 ? CHARS_LENGTH : 1); + } + return (res - SLAT) / PRIME1; + } + + /** + * 查找对应字符的index + * + * @param c 字符 + * @return index + */ + private static int findIndex(char c) { + for (int i = 0; i < CHARS_LENGTH; i++) { + if (CHARS[i] == c) { + return i; + } + } + return -1; + } +} + diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java index 507fb4b3e7..db5e95bccd 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java @@ -86,4 +86,12 @@ public interface AdminUserApi { */ void validateUserList(Collection ids); + /** + * 根据邀请码,获得用户 + * + * @param invitationCode 邀请码 + * @return 用户 + */ + AdminUserRespDTO getUserByInvitationCode(String invitationCode); + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java index 6af7bcd422..752122cce3 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java @@ -79,4 +79,11 @@ public class AdminUserApiImpl implements AdminUserApi { userService.validateUserList(ids); } + + @Override + public AdminUserRespDTO getUserByInvitationCode(String invitationCode) { + AdminUserDO user = userService.getUserByInvitaCode(invitationCode); + return BeanUtils.toBean(user, AdminUserRespDTO.class); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java index 1015bc0736..8802133046 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java @@ -169,4 +169,13 @@ public class UserController { return success(userService.importUserList(list, updateSupport)); } + @PutMapping("/gen-invite-code") + @Operation(summary = "生成邀请码") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:user:create')") + public CommonResult generateInviteCode(@RequestParam("id") Long id) { + userService.generateInviteCode(id); + return success(true); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java index 2837318f9a..760f2bbfd4 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserRespVO.java @@ -31,6 +31,9 @@ public class UserRespVO{ @Schema(description = "备注", example = "我是一个用户") private String remark; + @Schema(description = "邀请码", example = "1234567") + private String invitationCode; + @Schema(description = "部门ID", example = "我是一个用户") private Long deptId; @Schema(description = "部门名称", example = "IT 部") diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java index 2f07a3015a..e4a98f2944 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java @@ -93,4 +93,9 @@ public class AdminUserDO extends TenantBaseDO { */ private LocalDateTime loginDate; + /** + * 邀请码 + */ + private String invitationCode; + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java index c0c9be8e42..ed4bcc6f2c 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java @@ -48,4 +48,7 @@ public interface AdminUserMapper extends BaseMapperX { return selectList(AdminUserDO::getDeptId, deptIds); } + default AdminUserDO selectByInvitationCode(String invitationCode){ + return selectOne(AdminUserDO::getInvitationCode,invitationCode); + }; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java index 15564408d7..84c8238717 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java @@ -216,4 +216,18 @@ public interface AdminUserService { */ boolean isPasswordMatch(String rawPassword, String encodedPassword); + /** + * 获得指定邀请码的用户 + * + * @param invitationCode 邀请码 + * @return 用户 + */ + AdminUserDO getUserByInvitaCode(String invitationCode); + + /** + * 生成邀请码 + * + * @param id 用户编号 + */ + void generateInviteCode(Long id); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index 09e7b61e1f..cf902f6d12 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.exception.ServiceException; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.invite.InviteUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils; @@ -528,4 +529,33 @@ public class AdminUserServiceImpl implements AdminUserService { return passwordEncoder.encode(password); } + /** + * 获取邀请码对应的的用户 + * + * @param invitationCode 用户编号数组 + * @return 用户信息 + */ + @Override + public AdminUserDO getUserByInvitaCode(String invitationCode) { + return userMapper.selectByInvitationCode(invitationCode); + } + + /** + * 生成邀请码 + * + * @param id 用户编号 + */ + @Override + public void generateInviteCode(Long id) { + // 校验用户存在 + AdminUserDO dd = validateUserExists(id); + if(!StrUtil.isNotBlank(dd.getInvitationCode())) { + // 更新状态 + AdminUserDO updateObj = new AdminUserDO(); + updateObj.setId(id); + updateObj.setInvitationCode(InviteUtils.generateInviteCode(id)); + userMapper.updateById(updateObj); + } + + } } diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index efd53c84a5..6ac577f9e0 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -33,11 +33,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-member-biz + ${revision} + @@ -46,11 +46,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-bpm-biz + ${revision} + @@ -88,11 +88,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-crm-biz + ${revision} + diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 60564f094b..d5ac4323f7 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -47,7 +47,7 @@ spring: primary: master datasource: master: - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + url: jdbc:mysql://192.168.111.130:3306/ruoyi?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 @@ -55,7 +55,7 @@ spring: # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 # url: jdbc:kingbase8://127.0.0.1:54321/test # 人大金仓 KingbaseES 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/postgres # OpenGauss 连接的示例 - username: root + username: ruoyi password: 123456 # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 @@ -65,7 +65,7 @@ spring: # password: Yudao@2024 # OpenGauss 连接的示例 slave: # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true + url: jdbc:mysql://192.168.111.130:3306/ruoyi?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true username: root password: 123456