PAMapp/pages/login/index.vue
@@ -152,7 +152,7 @@ <el-button type="primary" v-if="(connectDevice === 'MOBILE' && onPhoneVerifyStep === 'INPUT_OTP') || (connectDevice === 'EMAIL' && onEmailVerifyResendStatus === 'CAN_RESEND')" :disabled="isLoginBtnDisabled" :disabled="isSubmitBtnDisabled" @click="login"> éåº </el-button> @@ -161,7 +161,7 @@ <el-dialog title="æ¡è¿æ°ä½¿ç¨è " :custom-class="'pam-register-dialog'" :visible.sync="registerDialogVisable" :visible.sync="registerDialogVisible" :fullscreen="true" :close-on-click-modal="false" :show-close="false" @@ -185,7 +185,8 @@ <el-row class="pt-10"> <div class="mdTxt pam-register-dialog__contract" @scroll="detectContructReadStatus"> ref="contract" @scroll="detectContractReadStatus"> <h3>èéåäººè³æåç¥äºé </h3> <p class="mt-10"> éµå®åäººè³æä¿è·æ³è¦å®ï¼å¨æ¨æä¾åäººè³æäºæ¬èåï¼ä¾æ³å @@ -248,15 +249,15 @@ </el-row> <el-row class="pt-30"> <div class="pam-agree-radio"> <label for="agreeControct" class="pam-radio" <label for="agreeContract" class="pam-radio" :class="{disabled: !isReadContract}"> <input type="radio" id="agreeControct" @click="agreeControct = !agreeControct" id="agreeContract" @click="agreeContract = !agreeContract" :disabled="!isReadContract" value="agreeControct"> <i :class="agreeControct ?'icon-checkbox-1': 'icon-checkbox'"></i>æåæä¸¦ç¹¼çº value="agreeContract"> <i :class="agreeContract ?'icon-checkbox-1': 'icon-checkbox'"></i>æåæä¸¦ç¹¼çº </label> </div> </el-row> @@ -264,7 +265,7 @@ <span slot="footer" class="dialog-footer"> <el-button type="primary" :disabled="!name || !agreeControct || !isReadContract" :disabled="!name || !agreeContract || !isReadContract" @click="applyAccount" >å»ºç«æ°å¸³è </el-button> @@ -320,7 +321,7 @@ <script lang="ts"> import { namespace } from 'nuxt-property-decorator'; import { Vue, Component } from 'vue-property-decorator'; import { Vue, Component, Ref } from 'vue-property-decorator'; import { LoginRequest, LoginVerify, loginVerify, OtpInfo, register, RegisterInfo, sendOtp } from '~/assets/ts/api/consultant'; import { Role } from '~/assets/ts/models/enum/Role'; @@ -330,6 +331,7 @@ export default class Login extends Vue { @roleStorage.Mutation storageIdToken!: (token:string) => void; @roleStorage.Mutation storageRole!: (role:string) => void; @Ref('contract') readonly contract!: any; connectDevice: 'MOBILE' | 'EMAIL' = 'MOBILE'; @@ -350,13 +352,13 @@ emailOtpInfo!: OtpInfo; name = ''; agreeControct = false; agreeContract = false; isReadContract = false; phoneSuccessConfirmVisable = false; emailOtpConfirmVisable = false; registerDialogVisable = false; registerDialogVisible = false; registerSuccessConfirmVisable = false; applyAccount_onAction = false; @@ -374,13 +376,19 @@ } } detectContructReadStatus(event: any): void { detectContractReadStatus(event: any): void { const scrollTop = Math.round(event.target.scrollTop); const height = event.target.scrollHeight - event.target.clientHeight; if (Math.floor(scrollTop/10) === (Math.floor(height/10))) { this.isReadContract = true; } }; get isSubmitBtnDisabled(): boolean { return this.connectDevice === 'MOBILE' ? (!this.otpCode || !this.phoneNumber || !this.phoneValid || !this.otpCounterSec) : (!this.emailOtpCode || !this.email || !this.emailValid || !this.emailCounterSec) } get phoneCounter() { let min = Math.floor(this.otpCounterSec / 60); @@ -480,7 +488,13 @@ this.storagePhoneOrEmail(this.setRegisterInfo()); }).catch(error => { if (error.response.status === 401) { this.registerDialogVisable = true; this.registerDialogVisible = true; setTimeout(() => { const isScrollBarNeedless = this.contract.scrollHeight <= this.contract.clientHeight; if (isScrollBarNeedless) { this.isReadContract = true; } }, 1000); } }) } @@ -648,6 +662,21 @@ } } .pam-register-dialog__contract { $DEVICE_EXTRA_HEIGHT: 42px; $ALIGN_PADDING: 60px; $TOP_CONTENT_HEIGHT: 186px; $BOTTOM_CONTENT_HEIGHT: 131px; max-height: calc(100vh - $DEVICE_EXTRA_HEIGHT - $ALIGN_PADDING - $TOP_CONTENT_HEIGHT - $BOTTOM_CONTENT_HEIGHT); overflow-y: scroll; border-radius: 6px; border: 1px solid #707070; padding: 20px; @include desktop { height: 335px; } } .pam-register-dialog__contract { $DEVICE_EXTRA_HEIGHT: 42px; $ALIGN_PADDING: 60px; PAMapp/plugins/filters/date.filter.ts
@@ -24,7 +24,7 @@ return compareDate.getFullYear() === today.getFullYear(); }; const minutes = date.getMinutes() > 10 ? date.getMinutes() : `0${date.getMinutes()}`; const minutes = date.getMinutes() > 9 ? date.getMinutes() : `0${date.getMinutes()}`; if (isThisYear(date)) { return isToday(date) pamapi/src/doc/«È¤áAPI/¨ú±oÓ¤H±b¸¹¸ê°T.txt
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,8 @@ http get: http://localhost:8080/api/customer/info response body: { "email": null, "phone": "0955555555", "name": "test999" } pamapi/src/doc/«È¤áAPI/§ó·sÓ¤H±b¸¹¸ê°T.txt
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,10 @@ http put: http://localhost:8080/api/customer/info request body: { "email": null, "phone": "0955555555", "name": "test998" } response http code: 204 (NO_CONTENT) pamapi/src/doc/µn¤JAPI/ÅU°Ýµn¤JÅçÃÒ½X¬yµ{.txt
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,29 @@ * å ç¼éåå¾é©è碼åç http get : http://localhost:8080/api/login/validate/get_img_code response content type: image/jpeg * å°é©è碼éè³å¾ç«¯é²è¡é©è http get : http://localhost:8080/api/login/validate/verify_img_code/{imgCode} ex: http://localhost:8080/api/login/validate/verify_img_code/4Rrcp response : true (é©èæå), false(é©è失æ) pamapi/src/main/java/com/pollex/pam/config/SecurityConfiguration.java
@@ -83,6 +83,7 @@ .antMatchers("/api/activate").permitAll() .antMatchers("/api/testLogin/**").permitAll() .antMatchers("/api/otp/**").permitAll() .antMatchers("/api/login/validate/**").permitAll() .antMatchers("/api/eService/authenticate").permitAll() .antMatchers("/api/account/reset-password/init").permitAll() .antMatchers("/api/account/reset-password/finish").permitAll() pamapi/src/main/java/com/pollex/pam/domain/Customer.java
@@ -4,49 +4,43 @@ import java.time.Instant; import java.util.Optional; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; import org.springframework.util.StringUtils; import com.fasterxml.jackson.annotation.JsonIgnore; import com.pollex.pam.enums.OtpLoginTypeEnum; @EntityListeners(AuditingEntityListener.class) @Entity @Table(name = "customer") public class Customer implements Serializable { /** * * */ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(name = "name") private String name; @Column(name = "phone") private String phone; @Column(name = "email") private String email; @Enumerated(value = EnumType.STRING) @Column(name = "contact_type") private OtpLoginTypeEnum contactType; @CreatedDate @Column(name = "created_date", updatable = false) @JsonIgnore @@ -112,7 +106,7 @@ public void setContactType(OtpLoginTypeEnum contactType) { this.contactType = contactType; } public String toAccountString() { return Optional.ofNullable(getPhone()) .filter(StringUtils::hasText) pamapi/src/main/java/com/pollex/pam/enums/CustomerDetailEnum.java
@@ -1,10 +1,8 @@ package com.pollex.pam.enums; public enum CustomerDetailEnum { ID("CustomerId"), NAME("CustomerName"), ACCOUNT("CustomerAccount"), CONTACT_TYPE("ContactType"); DB_ID("CustomerDBId"), NAME("CustomerName"); private final String value; pamapi/src/main/java/com/pollex/pam/security/SecurityUtils.java
@@ -107,10 +107,9 @@ return userDetails.get(ConsultantDetailEnum.AGENT_NO.getValue()); } // todo , should get id from user details public static Long getCustomerId() { public static Long getCustomerDBId() { Map<String, String> userDetails = getCurrentUserDetails(); return Long.parseLong(userDetails.get(CustomerDetailEnum.ID.getValue())); return Long.parseLong(userDetails.get(CustomerDetailEnum.DB_ID.getValue())); } public static Map<String, String> getCurrentUserDetails() { pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java
@@ -16,7 +16,6 @@ import com.pollex.pam.service.dto.AppointmentCustomerViewDTO; import com.pollex.pam.service.mapper.AppointmentCustomerViewMapper; import com.pollex.pam.service.mapper.AppointmentDTOMapper; import com.pollex.pam.service.mapper.AppointmentMapper; import com.pollex.pam.web.rest.errors.AppointmentNotFoundException; @Service @@ -40,7 +39,7 @@ public void customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) { Appointment appointment = appointmentDTOMapper.toAppointment(appointmentCreateDTO); appointment.setCustomerId(SecurityUtils.getCustomerId()); appointment.setCustomerId(SecurityUtils.getCustomerDBId()); appointment.setCommunicateStatus(ContactStatusEnum.RESERVED); appointmentRepository.save(appointment); } pamapi/src/main/java/com/pollex/pam/service/ConsultantService.java
@@ -44,7 +44,7 @@ AppointmentCustomerViewMapper appointmentCustomerViewMapper; public List<ConsultantDTO> getMyConsultantList() { Long userId = SecurityUtils.getCustomerId(); Long userId = SecurityUtils.getCustomerDBId(); return customerFavoriteConsultantRepository.findAllByCustomerId(userId) .stream() .map(consultantMapper::toDto) @@ -81,7 +81,7 @@ public void addConsultantToCustomList(AddConsultantParam param) { List<String> agentNoList = param.getAgentNoList(); List<Consultant> consultants = consultantRepository.findAllByAgentNoIn(agentNoList); Long userId = SecurityUtils.getCustomerId(); Long userId = SecurityUtils.getCustomerDBId(); consultants.forEach(consultant -> { boolean isConsultantInList = customerFavoriteConsultantRepository.findOneByCustomerIdAndConsultant(userId, consultant).isPresent(); @@ -107,7 +107,7 @@ } public void removeConsultantFromCustomList(String agentNo) { Long customId = SecurityUtils.getCustomerId(); Long customId = SecurityUtils.getCustomerDBId(); Consultant consultant = consultantRepository.findOneByAgentNo(agentNo).orElseThrow(ConsultantNotFoundException::new); CustomerFavoriteConsultant target = customerFavoriteConsultantRepository.findOneByCustomerIdAndConsultant(customId, consultant).orElse(null); pamapi/src/main/java/com/pollex/pam/service/CustomerAuthService.java
@@ -25,16 +25,16 @@ @Service @Transactional public class CustomerAuthService { @Autowired AuthenticationManagerBuilder authenticationManagerBuilder; @Autowired CustomerRepository customerRepository; @Autowired TokenProvider tokenProvider; public String authorize(Customer account, String indexKey, String otpCode) { OtpAccount otpAccount = OtpAccount.createOtpAccount(account, indexKey); OtpAuthenticationToken authenticationToken = new OtpAuthenticationToken( @@ -43,26 +43,25 @@ ); SecurityContextHolder.getContext().setAuthentication(authenticationToken); Authentication authentication = buildCustomerAuthToken(account, otpCode, indexKey); String jwt = tokenProvider.createToken(authentication, false); return jwt; } public UsernamePasswordAuthenticationToken buildCustomerAuthToken(Customer customer , String otpCode, String indexKey) { List<GrantedAuthority> grantedAuths = Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); final String account = customer.toAccountString(); UsernamePasswordAuthenticationToken authenticationToken = UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(account, otpCode, grantedAuths); Map<String, String> details = new HashMap<>(); details.put(CustomerDetailEnum.ID.getValue(), customer.getId().toString()); details.put(CustomerDetailEnum.DB_ID.getValue(), customer.getId().toString()); details.put(CustomerDetailEnum.NAME.getValue(), customer.getName()); details.put(CustomerDetailEnum.ACCOUNT.getValue(), account); // details.put(CustomerDetailEnum.CONTACT_TYPE.getValue(), customer.getContactType()); authenticationToken.setDetails(details); return authenticationToken; pamapi/src/main/java/com/pollex/pam/service/CustomerService.java
@@ -2,6 +2,9 @@ import java.util.Optional; import com.pollex.pam.security.SecurityUtils; import com.pollex.pam.service.dto.CustomerDTO; import com.pollex.pam.service.mapper.CustomerMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -18,31 +21,34 @@ @Service @Transactional public class CustomerService { @Autowired CustomerRepository customerRepository; @Autowired CustomerDTOMapper customerDTOMapper; @Autowired CustomerAuthService customerAuthService; @Autowired OtpTmpService otpTmpService; @Autowired CustomerMapper customerMapper; public Customer save(Customer customer) { return customerRepository.save(customer); } public Customer registerCustomer(CustomerRegisterDTO registDTO) { boolean isCustomerExist = checkCustomerExist(registDTO); if(isCustomerExist) { throw new UsernameAlreadyUsedException(); }else { String account = getCustomerAccount(registDTO); OtpTmp otpTmp = otpTmpService.findByAccountAndIndexKey(account, registDTO.getIndexKey()); if(otpTmp.getStatus() == OtpTmpStatusEnum.VERRIFIED) { Customer customer = customerDTOMapper.toCustomer(registDTO); @@ -54,9 +60,28 @@ + " => status: " + otpTmp.getStatus()); } } } public void updateLoggedCustomer(CustomerDTO customerDTO) { Long customerId = SecurityUtils.getCustomerDBId(); Customer customer = customerRepository.findById(customerId) .orElseThrow(() -> new UsernameNotFoundException("customerId which is from token is not found in customer db table, customer id = " + customerId)); customer.setEmail(customerDTO.getEmail()); customer.setPhone(customerDTO.getPhone()); customer.setName(customerDTO.getName()); customerRepository.save(customer); } public CustomerDTO getLoggedCustomerDTO() { Long customerId = SecurityUtils.getCustomerDBId(); Customer customer = customerRepository.findById(customerId) .orElseThrow(() -> new UsernameNotFoundException("customerId which is from token is not found in customer db table, customer id = " + customerId)); return customerMapper.toDto(customer); } private String getCustomerAccount(CustomerRegisterDTO registDTO) { return registDTO.getContactType() == OtpLoginTypeEnum.EMAIL?registDTO.getEmail():registDTO.getPhone(); } pamapi/src/main/java/com/pollex/pam/service/dto/CustomerDTO.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,31 @@ package com.pollex.pam.service.dto; public class CustomerDTO { private String email; private String phone; private String name; public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getName() { return name; } public void setName(String name) { this.name = name; } } pamapi/src/main/java/com/pollex/pam/service/mapper/CustomerMapper.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,19 @@ package com.pollex.pam.service.mapper; import com.pollex.pam.domain.Customer; import com.pollex.pam.service.dto.CustomerDTO; import org.springframework.stereotype.Service; @Service public class CustomerMapper { public CustomerDTO toDto(Customer customer) { CustomerDTO dto = new CustomerDTO(); dto.setEmail(customer.getEmail()); dto.setPhone(customer.getPhone()); dto.setName(customer.getName()); return dto; } } pamapi/src/main/java/com/pollex/pam/service/util/VerifyCodeUtil.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,68 @@ package com.pollex.pam.service.util; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.util.Random; public final class VerifyCodeUtil { public static String createcode() { String code = ""; code = ""; String randomRange = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";// Randomly generated character // range (0-9, a-z, A-Z) // The number of digits for generating the verification code (here 4 digits) for (int i = 0; i < 4; i++) { int index = (int) (Math.random() * 62);// Will produce a [0,62) number, excluding decimals char randomCode = randomRange.charAt(index); code = code + randomCode; } return code; } // 3. Generate pictures public static BufferedImage createimage(String code) { // The first 2 parameters are: width, height. The back is the image type // Create a BufferedImage object without transparent color, TYPE_INT_ARGB is // with transparent color BufferedImage bi = new BufferedImage(130, 50, BufferedImage.TYPE_INT_RGB); // 1. Get a canvas Graphics g = bi.getGraphics(); // 2. Add background color g.setColor(Color.WHITE); g.fillRect(0, 0, 130, 50); // 3. Add interference lines for (int i = 0; i < 10; i++) { Random r = new Random(); int red = r.nextInt(256); int green = r.nextInt(256); int blue = r.nextInt(256); Color c = new Color(red, green, blue); g.setColor(c); int x1 = r.nextInt(131); int y1 = r.nextInt(51); int x2 = r.nextInt(131); int y2 = r.nextInt(51); g.drawLine(x1, y1, x2, y2);// Draw a line // g.drawOval(x1, y1, x2, y2);//Draw a curve } // 3. Add text g.setColor(Color.BLACK); g.setFont(new Font(" ", Font.BOLD, 40)); // 4. Fill the text into the artboard g.drawString(code, 15, 40); // 5. Close the canvas g.dispose(); return bi; } } pamapi/src/main/java/com/pollex/pam/web/rest/ConsultantLoginValidateResource.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,62 @@ package com.pollex.pam.web.rest; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.imageio.ImageIO; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.pollex.pam.service.util.VerifyCodeUtil; @RestController @RequestMapping("/api/login/validate") public class ConsultantLoginValidateResource { @GetMapping("/get_img_code") public void getVerifyCodeImg(HttpServletResponse response, HttpServletRequest request) { try { String code = VerifyCodeUtil.createcode(); BufferedImage image = VerifyCodeUtil.createimage(code); // Return to the client in a stream response.setContentType("image/jpeg"); // response.setContentType("image/bmp"); ByteArrayOutputStream bt = new ByteArrayOutputStream(); // Convert pictures to byte stream ImageIO.write(image, "jpeg", bt); ServletOutputStream outputStream = response.getOutputStream(); outputStream.write(bt.toByteArray()); HttpSession session = request.getSession(); session.setAttribute("img_code", code); } catch (IOException e) { e.printStackTrace(); } } @GetMapping("/verify_img_code/{imgCode}") public boolean verifyImgCode(HttpServletResponse response, HttpServletRequest request, @PathVariable String imgCode) { HttpSession session = request.getSession(); String sessionImpCode = (String) session.getAttribute("img_code"); if (!StringUtils.hasText(sessionImpCode) || !StringUtils.hasText(imgCode)) { return false; } if (imgCode.equals(sessionImpCode)) { return true; } return false; } } pamapi/src/main/java/com/pollex/pam/web/rest/CustomerInfoResource.java
¤ñ¹ï·sÀÉ®× @@ -0,0 +1,33 @@ package com.pollex.pam.web.rest; import com.pollex.pam.service.CustomerService; import com.pollex.pam.service.dto.CustomerDTO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/customer/info") public class CustomerInfoResource { @Autowired CustomerService customerService; @GetMapping("") public ResponseEntity<CustomerDTO> getLoggedCustomerInfo() { return new ResponseEntity<>(customerService.getLoggedCustomerDTO(), HttpStatus.OK); } @PutMapping("") public ResponseEntity<Void> updateLoggedCustomerInfo(@RequestBody CustomerDTO customerDTO) { boolean hasEmail = StringUtils.hasText(customerDTO.getEmail()); boolean hasPhone = StringUtils.hasText(customerDTO.getPhone()); Assert.isTrue(hasEmail || hasPhone, "the email and the phone both are empty!"); customerService.updateLoggedCustomer(customerDTO); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } }