diff --git a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java index 76edabeae..493dbb58f 100644 --- a/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java +++ b/yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java @@ -13,6 +13,9 @@ public interface ErrorCodeConstants { ErrorCode USER_NOT_EXISTS = new ErrorCode(1004001000, "用户不存在"); ErrorCode USER_PASSWORD_FAILED = new ErrorCode(1004001001, "密码校验失败"); ErrorCode PROMOTER_NOT_EXISTS = new ErrorCode(1004001002, "推广员不存在"); + + ErrorCode PROMOTER_EXISTS = new ErrorCode(1004001003, "推广员存在"); + ErrorCode PROMOTER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1002003004, "导入推广员数据不能为空!"); // ========== AUTH 模块 1004003000 ========== ErrorCode AUTH_LOGIN_BAD_CREDENTIALS = new ErrorCode(1004003000, "登录失败,账号密码不正确"); ErrorCode AUTH_LOGIN_USER_DISABLED = new ErrorCode(1004003001, "登录失败,账号被禁用"); diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/PromoterController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/PromoterController.java index 8a651b0cd..05dba656b 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/PromoterController.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/PromoterController.java @@ -1,5 +1,8 @@ package cn.iocoder.yudao.module.member.controller.admin.promoter; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.module.system.enums.common.SexEnum; +import io.swagger.v3.oas.annotations.Parameters; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -27,6 +30,7 @@ import cn.iocoder.yudao.module.member.controller.admin.promoter.vo.*; import cn.iocoder.yudao.module.member.dal.dataobject.promoter.PromoterDO; import cn.iocoder.yudao.module.member.convert.promoter.PromoterConvert; import cn.iocoder.yudao.module.member.service.promoter.PromoterService; +import org.springframework.web.multipart.MultipartFile; @Tag(name = "管理后台 - 推广员") @RestController @@ -98,5 +102,27 @@ public class PromoterController { List datas = PromoterConvert.INSTANCE.convertList02(list); ExcelUtils.write(response, "推广员.xls", "数据", PromoterExcelVO.class, datas); } + @GetMapping("/get-import-template") + @Operation(summary = "获得导入推广员模板") + public void importTemplate(HttpServletResponse response) throws IOException { + // 手动创建导出 demo + List list = Arrays.asList( + PromoterImportExcelVO.builder().nickName("yunai").orgName("创盈云网络>重庆总公司>研发部门").mobile("15601691300").build() + ); + // 输出 + ExcelUtils.write(response, "推广员导入模板.xls", "推广员列表", PromoterImportExcelVO.class, list); + } + @PostMapping("/import") + @Operation(summary = "导入推广员") + @Parameters({ + @Parameter(name = "file", description = "Excel 文件", required = true), + @Parameter(name = "updateSupport", description = "是否支持更新,默认为 false", example = "true") + }) +// @PreAuthorize("@ss.hasPermission('system:user:import')") + public CommonResult importExcel(@RequestParam("file") MultipartFile file, + @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception { + List list = ExcelUtils.read(file, PromoterImportExcelVO.class); + return success(promoterService.importUserList(list, updateSupport)); + } } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportExcelVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportExcelVO.java new file mode 100644 index 000000000..7684fc9ee --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportExcelVO.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.member.controller.admin.promoter.vo; + +import cn.iocoder.yudao.framework.common.validation.Mobile; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotEmpty; + +/** + * 推广员 Excel VO + * + * @author 创盈云 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Accessors(chain = false) +public class PromoterImportExcelVO { + + @ExcelProperty("手机号码") + @Mobile + @NotEmpty(message = "手机号码不能为空") + private String mobile; + @ExcelProperty("组织全称") + @NotEmpty(message = "组织全称不能为空") + private String orgName; + + @ExcelProperty("姓名") + @NotEmpty(message = "姓名不能为空") + private String nickName; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportRespVO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportRespVO.java new file mode 100644 index 000000000..9940158a6 --- /dev/null +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/admin/promoter/vo/PromoterImportRespVO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.member.controller.admin.promoter.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Builder; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 推广员导入 Response VO") +@Data +@Builder +public class PromoterImportRespVO { + + @Schema(description = "导入成功的推广员数组", required = true) + private List createUsernames; + + @Schema(description = "更新成功的推广员数组", required = true) + private List updateUsernames; + + @Schema(description = "导入失败的推广员", required = true) + private Map failureUsernames; + +} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/promoter/PromoterDO.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/promoter/PromoterDO.java index 57eb9db96..01d631f2d 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/promoter/PromoterDO.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/promoter/PromoterDO.java @@ -32,7 +32,7 @@ public class PromoterDO implements Serializable { /** * 组织id */ - private String orgId; + private Long orgId; /** * 会员id */ diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterService.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterService.java index a648673de..756dadfe3 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterService.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterService.java @@ -67,4 +67,13 @@ public interface PromoterService { */ List getPromoterList(PromoterExportReqVO exportReqVO); + /** + * 批量导入推广员列表 + * + * @param importUsers 批量导入推广员列表 + * @param isUpdateSupport 是否支持更新 + * @return 导入结果 + */ + PromoterImportRespVO importUserList(List importUsers, boolean isUpdateSupport); + } diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterServiceImpl.java index d168f73eb..3e47b5889 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/promoter/PromoterServiceImpl.java @@ -1,10 +1,19 @@ package cn.iocoder.yudao.module.member.service.promoter; +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.exception.ServiceException; +import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO; import cn.iocoder.yudao.module.member.service.user.MemberUserService; +import cn.iocoder.yudao.module.system.api.dept.DeptApi; +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import javax.annotation.Resource; +import javax.validation.Validator; + import org.springframework.validation.annotation.Validated; import java.util.*; @@ -18,6 +27,9 @@ import cn.iocoder.yudao.module.member.dal.mysql.promoter.PromoterMapper; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.*; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_IMPORT_LIST_IS_EMPTY; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_USERNAME_EXISTS; +import static java.util.stream.Collectors.toMap; /** * 推广员 Service 实现类 @@ -30,10 +42,14 @@ public class PromoterServiceImpl implements PromoterService { @Resource private PromoterMapper promoterMapper; - + @Resource + private DeptApi deptApi; + @Resource + private Validator validator; @Resource private MemberUserService memberUserService; - + @Resource + private PasswordEncoder passwordEncoder; @Override public Long createPromoter(PromoterCreateReqVO createReqVO) { @@ -45,11 +61,16 @@ public class PromoterServiceImpl implements PromoterService { memberUserDO.setNickname(createReqVO.getNickName()); memberUserDO.setMobile(createReqVO.getMobile()); memberUserDO.setStatus(createReqVO.getStatus()); + memberUserDO.setPassword(createReqVO.getMobile().substring(createReqVO.getMobile().length()-6)); memberUserService.createUserIfAbsent(createReqVO.getMobile(),createReqVO.getNickName(),getClientIP()); } // 插入 PromoterDO promoter = PromoterConvert.INSTANCE.convert(createReqVO); promoter.setTenantId(SecurityFrameworkUtils.getLoginUser().getTenantId()); + Long count = promoterMapper.selectCount(Wrappers.lambdaQuery(PromoterDO.class).eq(PromoterDO::getUserId,memberUserDO.getId())); + if(count>0){ + throw new ServiceException(PROMOTER_EXISTS); + } promoter.setUserId(memberUserDO.getId()); promoterMapper.insert(promoter); // 返回 @@ -99,4 +120,68 @@ public class PromoterServiceImpl implements PromoterService { return promoterMapper.selectList(exportReqVO); } + /** + * 批量导入推广员列表 + * + * @param importUsers 批量导入推广员列表 + * @param isUpdateSupport 是否支持更新 + * @return 导入结果 + */ + @Override + public PromoterImportRespVO importUserList(List importUsers, boolean isUpdateSupport) { + if (CollUtil.isEmpty(importUsers)) { + throw exception(PROMOTER_IMPORT_LIST_IS_EMPTY); + } + PromoterImportRespVO respVO = PromoterImportRespVO.builder().createUsernames(new ArrayList<>()) + .updateUsernames(new ArrayList<>()).failureUsernames(new LinkedHashMap<>()).build(); + List deptRespDTOList = deptApi.getDeptList(); + Map nameList = deptRespDTOList.stream().collect(toMap(DeptRespDTO::getParentOrganizationName, value -> value,(value1,value2)->value1)); + importUsers.forEach(importUser -> { + try { + ValidationUtils.validate(validator,importUser); + } catch (ServiceException ex) { + respVO.getFailureUsernames().put(importUser.getNickName(), ex.getMessage()); + return; + } + //判断手机号是否注册 + MemberUserDO memberUserDO = memberUserService.getUserByMobile(importUser.getMobile()); + if(memberUserDO==null){ + //创建用户 + memberUserDO = new MemberUserDO(); + memberUserDO.setNickname(importUser.getNickName()); + memberUserDO.setMobile(importUser.getMobile()); + memberUserDO.setPassword(importUser.getMobile().substring(importUser.getMobile().length()-6)); + memberUserDO.setStatus(1); + memberUserService.createUserIfAbsent(importUser.getMobile(),importUser.getNickName(),getClientIP()); + } + // 插入 + PromoterDO promoter = new PromoterDO(); + promoter.setTenantId(SecurityFrameworkUtils.getLoginUser().getTenantId()); + promoter.setUserId(memberUserDO.getId()); + Long count = promoterMapper.selectCount(Wrappers.lambdaQuery(PromoterDO.class).eq(PromoterDO::getUserId,memberUserDO.getId())); + if(count>0){ + respVO.getFailureUsernames().put(importUser.getNickName(), "已经是推广员"); + return; + } + DeptRespDTO deptRespDTO = nameList.get(importUser.getOrgName()); + if(deptRespDTO==null){ + respVO.getFailureUsernames().put(importUser.getNickName(), "组织不存在"); + return; + } + promoter.setOrgId(deptRespDTO.getId()); + promoterMapper.insert(promoter); + respVO.getCreateUsernames().add(importUser.getNickName()); + return; + }); + return respVO; + } + /** + * 对密码进行加密 + * + * @param password 密码 + * @return 加密后的密码 + */ + private String encodePassword(String password) { + return passwordEncoder.encode(password); + } } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java index c3d143e46..ebefd9756 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java @@ -31,6 +31,14 @@ public interface DeptApi { */ List getDeptList(Collection ids); + /** + * 获得部门信息数组 + * + * @param ids 部门编号数组 + * @return 部门信息数组 + */ + List getDeptList(); + /** * 校验部门们是否有效。如下情况,视为无效: * 1. 部门编号不存在 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java index d3e66fdd8..531bff4d4 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/dto/DeptRespDTO.java @@ -34,4 +34,14 @@ public class DeptRespDTO { */ private Integer status; + /** + * 父级组织链 + */ + private String parentOrganizationIds; + + /** + * 父级组织链名称 + */ + private String parentOrganizationName; + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java index c1676ca53..bf679107d 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.api.dept; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.convert.dept.DeptConvert; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.service.dept.DeptService; @@ -32,7 +33,11 @@ public class DeptApiImpl implements DeptApi { List depts = deptService.getDeptList(ids); return DeptConvert.INSTANCE.convertList03(depts); } - + @Override + public List getDeptList() { + List depts = deptService.getDeptList(new DeptListReqVO()); + return DeptConvert.INSTANCE.convertList03(depts); + } @Override public void validateDeptList(Collection ids) { deptService.validateDeptList(ids); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java index 512a4a761..de547fff7 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantPageReqVO.java @@ -1,10 +1,12 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.validation.Mobile; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import org.hibernate.validator.constraints.Length; import org.springframework.format.annotation.DateTimeFormat; import java.time.LocalDateTime; @@ -24,11 +26,24 @@ public class TenantPageReqVO extends PageParam { private String contactName; @Schema(description = "联系手机", example = "15601691300") + @Mobile private String contactMobile; @Schema(description = "租户状态(0正常 1停用)", example = "1") private Integer status; + /** + * 销售负责人 + */ + @Schema(description = "销售负责人", example = "https://www.iocoder.cn") + @Length( max = 10, message = "销售负责人长度为 {max}位") + private String saleContactName; + /** + * 销售负责人联系电话 + */ + @Schema(description = "销售负责人联系电话", example = "https://www.iocoder.cn") + @Mobile + private String saleContactMobile; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @Schema(description = "创建时间") private LocalDateTime[] createTime; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java index 8731e4628..611e3af0e 100755 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java @@ -23,6 +23,8 @@ public interface TenantMapper extends BaseMapperX { .likeIfPresent(TenantDO::getName, reqVO.getName()) .likeIfPresent(TenantDO::getContactName, reqVO.getContactName()) .likeIfPresent(TenantDO::getContactMobile, reqVO.getContactMobile()) + .likeIfPresent(TenantDO::getSaleContactName, reqVO.getSaleContactName()) + .likeIfPresent(TenantDO::getSaleContactMobile, reqVO.getSaleContactMobile()) .eqIfPresent(TenantDO::getStatus, reqVO.getStatus()) .betweenIfPresent(TenantDO::getCreateTime, reqVO.getCreateTime()) .orderByDesc(TenantDO::getId)); diff --git a/yudao-ui-admin/.env.dev b/yudao-ui-admin/.env.dev index 26b38c703..a836d5806 100644 --- a/yudao-ui-admin/.env.dev +++ b/yudao-ui-admin/.env.dev @@ -5,7 +5,7 @@ ENV = 'development' VUE_APP_TITLE = 创盈商户管理系统 # 芋道管理系统/开发环境 -VUE_APP_BASE_API = 'http://192.168.1.147:48080' +VUE_APP_BASE_API = 'http://192.168.2.71:48080' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/yudao-ui-admin/src/api/member/promoter.js b/yudao-ui-admin/src/api/member/promoter.js new file mode 100644 index 000000000..07abc6860 --- /dev/null +++ b/yudao-ui-admin/src/api/member/promoter.js @@ -0,0 +1,62 @@ +import request from '@/utils/request' + +// 创建推广员 +export function createPromoter(data) { + return request({ + url: '/member/promoter/create', + method: 'post', + data: data + }) +} + +// 更新推广员 +export function updatePromoter(data) { + return request({ + url: '/member/promoter/update', + method: 'put', + data: data + }) +} + +// 删除推广员 +export function deletePromoter(id) { + return request({ + url: '/member/promoter/delete?id=' + id, + method: 'delete' + }) +} + +// 获得推广员 +export function getPromoter(id) { + return request({ + url: '/member/promoter/get?id=' + id, + method: 'get' + }) +} + +// 获得推广员分页 +export function getPromoterPage(query) { + return request({ + url: '/member/promoter/page', + method: 'get', + params: query + }) +} + +// 导出推广员 Excel +export function exportPromoterExcel(query) { + return request({ + url: '/member/promoter/export-excel', + method: 'get', + params: query, + responseType: 'blob' + }) +} +// 下载用户导入模板 +export function importTemplate() { + return request({ + url: '/member/promoter/get-import-template', + method: 'get', + responseType: 'blob' + }) +} diff --git a/yudao-ui-admin/src/views/member/promoter/index.vue b/yudao-ui-admin/src/views/member/promoter/index.vue new file mode 100644 index 000000000..7ba9a6739 --- /dev/null +++ b/yudao-ui-admin/src/views/member/promoter/index.vue @@ -0,0 +1,289 @@ + + +