From 1ee612674e5c8ffb6e1313e8b4e16de8e43404ed Mon Sep 17 00:00:00 2001 From: shahaibo <1023316923@qq.com> Date: Fri, 11 Apr 2025 10:10:38 +0800 Subject: [PATCH] =?UTF-8?q?TASK:=E6=A8=A1=E5=9D=97=E5=8C=96=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/da/controller/AccountController.java | 27 ++- .../ai/da/model/dto/SubAccountPageDTO.java | 6 + .../ai/da/model/dto/SubAccountPageQuery.java | 13 + .../com/ai/da/service/AccountService.java | 12 +- .../da/service/impl/AccountServiceImpl.java | 223 ++++++++++++++---- .../files/sub_account_import_template.xlsx | Bin 0 -> 10752 bytes 6 files changed, 232 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/ai/da/model/dto/SubAccountPageQuery.java create mode 100644 src/main/resources/files/sub_account_import_template.xlsx diff --git a/src/main/java/com/ai/da/controller/AccountController.java b/src/main/java/com/ai/da/controller/AccountController.java index 5a294b84..aef2353f 100644 --- a/src/main/java/com/ai/da/controller/AccountController.java +++ b/src/main/java/com/ai/da/controller/AccountController.java @@ -12,6 +12,8 @@ import com.ai.da.model.vo.AccountPreLoginVO; import com.ai.da.model.vo.BindEmailVO; import com.ai.da.model.vo.PersonalHomepageVO; import com.ai.da.service.AccountService; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.minio.errors.MinioException; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; @@ -22,10 +24,13 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; +import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; @Api(tags = "Account模块") @@ -273,16 +278,22 @@ public class AccountController { @PostMapping("enterpriseLogin") @ApiOperation(value = "企业登录") - public Response enterpriseLogin(@Valid @RequestBody AccountLoginDTO accountDTO) { + public Response enterpriseLogin(@Valid @RequestBody AccountLoginDTO accountDTO) { return Response.success(accountService.enterpriseLogin(accountDTO)); } @PostMapping("schoolLogin") @ApiOperation(value = "学校登录") - public Response schoolLogin(@Valid @RequestBody AccountLoginDTO accountDTO) { + public Response schoolLogin(@Valid @RequestBody AccountLoginDTO accountDTO) { return Response.success(accountService.schoolLogin(accountDTO)); } + @PostMapping("organizationNameSearch") + @ApiOperation(value = "组织名模糊查询") + public Response> organizationNameSearch(@RequestParam("type") String type, @RequestParam("name") String name) { + return Response.success(accountService.organizationNameSearch(type, name)); + } + @PostMapping("addOrUpdateSubAccount") @ApiOperation(value = "子账号新增") public Response addSubAccount(@Valid @RequestBody AddSubAccountDTO addSubAccountDTO) { @@ -348,4 +359,16 @@ public class AccountController { public Response updateUserInfo(@Valid @RequestBody UpdateUserInfoDTO updateUserInfoDTO) { return Response.success(accountService.updateUserInfo(updateUserInfoDTO)); } + + @GetMapping("/subAccountImportExcelDownload") + @ApiOperation(value = "模板下载") + public void subAccountImportExcelDownload(HttpServletResponse response) { + accountService.subAccountImportExcelDownload(response); + } + + @PostMapping("/subAccountImport") + @ApiOperation(value = "模板导入") + public Response subAccountImport(@RequestParam("file") MultipartFile file) { + return Response.success(accountService.subAccountImport(file)); + } } diff --git a/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java b/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java index f5dcc995..7a57d7b5 100644 --- a/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java +++ b/src/main/java/com/ai/da/model/dto/SubAccountPageDTO.java @@ -5,5 +5,11 @@ import lombok.Data; @Data public class SubAccountPageDTO extends PageQueryBaseVo { + private String startTime; + + private String endTime; + + private String email; + private String userName; } diff --git a/src/main/java/com/ai/da/model/dto/SubAccountPageQuery.java b/src/main/java/com/ai/da/model/dto/SubAccountPageQuery.java new file mode 100644 index 00000000..0db2954f --- /dev/null +++ b/src/main/java/com/ai/da/model/dto/SubAccountPageQuery.java @@ -0,0 +1,13 @@ +package com.ai.da.model.dto; + +import com.ai.da.model.vo.PageQueryBaseVo; + +public class SubAccountPageQuery extends PageQueryBaseVo { + private String startTime; + + private String endTime; + + private String email; + + private String userName; +} diff --git a/src/main/java/com/ai/da/service/AccountService.java b/src/main/java/com/ai/da/service/AccountService.java index 4aa90811..9b7ff681 100644 --- a/src/main/java/com/ai/da/service/AccountService.java +++ b/src/main/java/com/ai/da/service/AccountService.java @@ -14,9 +14,11 @@ import com.baomidou.mybatisplus.extension.service.IService; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.util.List; import java.util.Map; +import java.util.Set; /** * 服务类 @@ -194,9 +196,9 @@ public interface AccountService extends IService { void temporaryUpgrade(); - AccountLoginVO enterpriseLogin(AccountLoginDTO accountDTO); + AccountPreLoginVO enterpriseLogin(AccountLoginDTO accountDTO); - AccountLoginVO schoolLogin(AccountLoginDTO accountDTO); + AccountPreLoginVO schoolLogin(AccountLoginDTO accountDTO); Boolean addSubAccount(AddSubAccountDTO addSubAccountDTO); @@ -225,4 +227,10 @@ public interface AccountService extends IService { void updateUserRoleAndCredits(Long accountId, String type); Boolean updateUserInfo(UpdateUserInfoDTO updateUserInfoDTO); + + void subAccountImportExcelDownload(HttpServletResponse response); + + Boolean subAccountImport(MultipartFile file); + + Set organizationNameSearch(String type, String name); } diff --git a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java index 46eb8762..aee95277 100644 --- a/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/AccountServiceImpl.java @@ -38,6 +38,7 @@ import io.netty.util.internal.StringUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -46,10 +47,14 @@ import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.sql.DataSource; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.math.BigDecimal; +import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.sql.Connection; @@ -176,7 +181,7 @@ public class AccountServiceImpl extends ServiceImpl impl // Assert.isTrue(StringUtils.isNotBlank(accountLoginDTO.getEmail()), "Please input a email !"); // Assert.isTrue(StringUtils.isNotBlank(accountLoginDTO.getEmailVerifyCode()), "Please input the email verification code !"); log.info("aida确认登入###accountLoginDTO###{}", JSON.toJSONString(accountLoginDTO)); - Account accountExist = getOneByEmail(accountLoginDTO.getEmail().trim()); + Account accountExist = getOneByEmailAndOrganizationName(accountLoginDTO.getEmail().trim(), accountLoginDTO.getOrganizationName()); LoginTypeEnum accountType = LoginTypeEnum.of(accountLoginDTO.getLoginType()); if (Objects.isNull(accountType)) { throw new BusinessException("unknown.login.type"); @@ -237,6 +242,19 @@ public class AccountServiceImpl extends ServiceImpl impl return response; } + private Account getOneByEmailAndOrganizationName(String email, String organizationName) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("BINARY user_email", email); + if (StringUtils.isNotBlank(organizationName)) { + queryWrapper.eq("organizationName", organizationName); + } + List accountList = accountMapper.selectList(queryWrapper); + if (CollectionUtil.isEmpty(accountList)) { + throw new BusinessException("email.does.not.exist", ResultEnum.PROMPT.getCode()); + } + return accountList.get(0); + } + private void validateUserValidaExpire(Account account) { Long currentTime = new Date().getTime(); if (account.getSystemUser().equals(0)){ @@ -2143,7 +2161,7 @@ public class AccountServiceImpl extends ServiceImpl impl @Override - public AccountLoginVO enterpriseLogin(AccountLoginDTO accountDTO) { + public AccountPreLoginVO enterpriseLogin(AccountLoginDTO accountDTO) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(Account::getUserEmail, accountDTO.getEmail()); qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName()); @@ -2157,34 +2175,58 @@ public class AccountServiceImpl extends ServiceImpl impl throw new BusinessException("Password error."); } Account account = accounts.get(0); - AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class); - response.setEmail(account.getUserEmail()); - String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId())); - if (StringUtils.isNotBlank(token)) { - //用户已登入 - response.setToken(token); + + validateUserValidaExpire(account); + if ("Third-000000".equals(account.getUserPassword())) { + account.setUserPassword(accountDTO.getPassword()); + accountMapper.updateById(account); } else { - response.setToken(createAccountToken(account)); + if (!account.getUserPassword().equals(accountDTO.getPassword())) { + throw new BusinessException("password.error", ResultEnum.PROMPT.getCode()); + } } - response.setUserId(account.getId()); - response.setSystemUser(account.getSystemUser()); - // 设置头像 - String avatar; - if (StringUtil.isNullOrEmpty(account.getAvatar())){ - avatar = CommonConstant.DEFAULT_AVATAR; - }else { - avatar = account.getAvatar(); + /*发送邮件*/ + AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of("LOGIN"); + String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L); + LocalCacheUtils.setVerifyCodeCache( + "LOGIN_" + accountDTO.getEmail(), randomVerifyCode); + Boolean result = Boolean.FALSE; + result = SendEmailUtil.send(accountDTO.getEmail(), null, + SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode); + if (!result) { + throw new BusinessException("failed.to.send.mail"); } - response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); - //判断是否常用ip 不是则发邮件提示 -// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account); - return response; + return new AccountPreLoginVO(account.getId()); + + +// AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class); +// response.setEmail(account.getUserEmail()); +// String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId())); +// if (StringUtils.isNotBlank(token)) { +// //用户已登入 +// response.setToken(token); +// } else { +// response.setToken(createAccountToken(account)); +// } +// response.setUserId(account.getId()); +// response.setSystemUser(account.getSystemUser()); +// // 设置头像 +// String avatar; +// if (StringUtil.isNullOrEmpty(account.getAvatar())){ +// avatar = CommonConstant.DEFAULT_AVATAR; +// }else { +// avatar = account.getAvatar(); +// } +// response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); +// response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); +// response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); +// //判断是否常用ip 不是则发邮件提示 +//// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account); +// return response; } @Override - public AccountLoginVO schoolLogin(AccountLoginDTO accountDTO) { + public AccountPreLoginVO schoolLogin(AccountLoginDTO accountDTO) { QueryWrapper qw = new QueryWrapper<>(); qw.lambda().eq(Account::getUserEmail, accountDTO.getEmail()); qw.lambda().eq(Account::getOrganizationName, accountDTO.getOrganizationName()); @@ -2198,30 +2240,53 @@ public class AccountServiceImpl extends ServiceImpl impl throw new BusinessException("Password error."); } Account account = accounts.get(0); - AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class); - response.setEmail(account.getUserEmail()); - String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId())); - if (StringUtils.isNotBlank(token)) { - //用户已登入 - response.setToken(token); + + validateUserValidaExpire(account); + if ("Third-000000".equals(account.getUserPassword())) { + account.setUserPassword(accountDTO.getPassword()); + accountMapper.updateById(account); } else { - response.setToken(createAccountToken(account)); + if (!account.getUserPassword().equals(accountDTO.getPassword())) { + throw new BusinessException("password.error", ResultEnum.PROMPT.getCode()); + } } - response.setUserId(account.getId()); - response.setSystemUser(account.getSystemUser()); - // 设置头像 - String avatar; - if (StringUtil.isNullOrEmpty(account.getAvatar())){ - avatar = CommonConstant.DEFAULT_AVATAR; - }else { - avatar = account.getAvatar(); + /*发送邮件*/ + AuthenticationOperationTypeEnum authenticationOperationTypeEnum = AuthenticationOperationTypeEnum.of("LOGIN"); + String randomVerifyCode = RandomsUtil.generateVerifyCode(100000L, 999999L); + LocalCacheUtils.setVerifyCodeCache( + "LOGIN_" + accountDTO.getEmail(), randomVerifyCode); + Boolean result = Boolean.FALSE; + result = SendEmailUtil.send(accountDTO.getEmail(), null, + SendEmailUtil.LOGIN_TEMPLATE_ID, randomVerifyCode); + if (!result) { + throw new BusinessException("failed.to.send.mail"); } - response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); - response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); - response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); - //判断是否常用ip 不是则发邮件提示 -// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account); - return response; + return new AccountPreLoginVO(account.getId()); + +// AccountLoginVO response = CopyUtil.copyObject(account, AccountLoginVO.class); +// response.setEmail(account.getUserEmail()); +// String token = LocalCacheUtils.getTokenCache(String.valueOf(account.getId())); +// if (StringUtils.isNotBlank(token)) { +// //用户已登入 +// response.setToken(token); +// } else { +// response.setToken(createAccountToken(account)); +// } +// response.setUserId(account.getId()); +// response.setSystemUser(account.getSystemUser()); +// // 设置头像 +// String avatar; +// if (StringUtil.isNullOrEmpty(account.getAvatar())){ +// avatar = CommonConstant.DEFAULT_AVATAR; +// }else { +// avatar = account.getAvatar(); +// } +// response.setAvatar(minioUtil.getPreSignedUrl(avatar, CommonConstant.MINIO_IMAGE_EXPIRE_TIME)); +// response.setFolloweeCount(portfolioService.getFolloweeCount(account.getId())); +// response.setFollowerCount(portfolioService.getFollowerCount(account.getId())); +// //判断是否常用ip 不是则发邮件提示 +//// calculateExceptionIp(RequestInfoUtil.getIpAddress(request), account); +// return response; } @Override @@ -2275,6 +2340,18 @@ public class AccountServiceImpl extends ServiceImpl impl QueryWrapper qw = new QueryWrapper<>(); qw.lambda().ne(Account::getId, account.getId()); qw.lambda().eq(Account::getOrganizationName, account.getOrganizationName()); + if (StringUtils.isNotBlank(subAccountPageDTO.getStartTime())) { + qw.lambda().ge(Account::getCreateDate, subAccountPageDTO.getStartTime()); + } + if (StringUtils.isNotBlank(subAccountPageDTO.getEndTime())) { + qw.lambda().le(Account::getCreateDate, subAccountPageDTO.getEndTime()); + } + if (StringUtils.isNotBlank(subAccountPageDTO.getEmail())) { + qw.lambda().like(Account::getUserEmail, subAccountPageDTO.getEmail()); + } + if (StringUtils.isNotBlank(subAccountPageDTO.getUserName())) { + qw.lambda().like(Account::getUserName, subAccountPageDTO.getUserName()); + } // 执行分页查询 IPage page = accountMapper.selectPage(new Page<>(subAccountPageDTO.getPage(), subAccountPageDTO.getSize()), qw); @@ -2837,4 +2914,60 @@ public class AccountServiceImpl extends ServiceImpl impl accountMapper.updateById(account); return Boolean.TRUE; } + + @Override + public void subAccountImportExcelDownload(HttpServletResponse response) { + // 文件名 + String fileName = "sub_account_import_template.xlsx"; + + // 获取文件输入流 + try (InputStream inputStream = new ClassPathResource("files/" + fileName).getInputStream(); + OutputStream outputStream = response.getOutputStream()) { + + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); + + // 写出数据 + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + outputStream.flush(); + + } catch (IOException e) { + throw new RuntimeException("下载模板文件失败", e); + } + } + + @Override + public Boolean subAccountImport(MultipartFile file) { + return null; + } + + @Override + public Set organizationNameSearch(String type, String name) { + QueryWrapper qw = new QueryWrapper<>(); + qw.lambda().like(Account::getOrganizationName, name); + if (type.equals("School")) { + Set schoolList = new HashSet<>(); + schoolList.add(7L); + schoolList.add(8L); + qw.lambda().in(Account::getSystemUser, schoolList); + } + if (type.equals("Enterprise")) { + Set schoolList = new HashSet<>(); + schoolList.add(5L); + schoolList.add(6L); + qw.lambda().in(Account::getSystemUser, schoolList); + } + List accountList = accountMapper.selectList(qw); + if (CollectionUtil.isNotEmpty(accountList)) { + return accountList.stream().map(Account::getOrganizationName).collect(Collectors.toSet()); + } + return new HashSet<>(); + } + } diff --git a/src/main/resources/files/sub_account_import_template.xlsx b/src/main/resources/files/sub_account_import_template.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..a891af8d348c294d196930b24d35be02eae5acfc GIT binary patch literal 10752 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 iYXIMe-=O^=