瀏覽代碼

feat:完成客户端家长登入功能

sakura 1 年之前
父節點
當前提交
616b53b580
共有 23 個文件被更改,包括 645 次插入205 次删除
  1. 83 4
      school-in-out-admin/src/main/java/com/schoolinout/web/controller/api/Api_ParentController.java
  2. 87 0
      school-in-out-common/src/main/java/com/schoolinout/common/core/domain/entity/ParentLogin.java
  3. 85 85
      school-in-out-common/src/main/java/com/schoolinout/common/core/domain/entity/SysDept.java
  4. 63 72
      school-in-out-common/src/main/java/com/schoolinout/common/core/domain/model/LoginUser.java
  5. 27 1
      school-in-out-framework/src/main/java/com/schoolinout/framework/config/SecurityConfig.java
  6. 1 1
      school-in-out-framework/src/main/java/com/schoolinout/framework/web/exception/GlobalExceptionHandler.java
  7. 48 0
      school-in-out-framework/src/main/java/com/schoolinout/framework/web/service/ParentLoginUserDetailsServiceImpl.java
  8. 16 22
      school-in-out-framework/src/main/java/com/schoolinout/framework/web/service/UserDetailsServiceImpl.java
  9. 14 0
      school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiParentMapper.java
  10. 15 0
      school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiParentStudentRelationMapper.java
  11. 14 0
      school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiStudentMapper.java
  12. 17 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/IApiParentService.java
  13. 12 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/IApiParentStudentRelationService.java
  14. 18 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/IApiStudentService.java
  15. 23 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiParentServiceImpl.java
  16. 23 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiParentStudentRelationServiceImpl.java
  17. 23 0
      school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiStudentServiceImpl.java
  18. 0 8
      school-in-out-system/src/main/java/com/schoolinout/system/service/IApiParentService.java
  19. 0 12
      school-in-out-system/src/main/java/com/schoolinout/system/service/impl/ApiParentServiceImpl.java
  20. 32 0
      school-in-out-system/src/main/resources/mapper/api/ApiParentMapper.xml
  21. 13 0
      school-in-out-system/src/main/resources/mapper/api/ApiParentStudentRelationMapper.xml
  22. 26 0
      school-in-out-system/src/main/resources/mapper/api/ApiStudentMapper.xml
  23. 5 0
      school-in-out-ui/src/views/system/teacher/index.vue

+ 83 - 4
school-in-out-admin/src/main/java/com/schoolinout/web/controller/api/Api_ParentController.java

@@ -1,19 +1,37 @@
 package com.schoolinout.web.controller.api;
 
+import com.schoolinout.api.service.IApiParentService;
+import com.schoolinout.api.service.IApiParentStudentRelationService;
+import com.schoolinout.api.service.IApiStudentService;
 import com.schoolinout.common.core.domain.AjaxResult;
+import com.schoolinout.common.core.domain.entity.ParentLogin;
 import com.schoolinout.common.core.domain.entity.SysDictData;
+import com.schoolinout.common.core.domain.model.LoginUser;
+import com.schoolinout.common.core.redis.RedisCache;
+import com.schoolinout.common.exception.ServiceException;
+import com.schoolinout.common.exception.user.UserPasswordNotMatchException;
 import com.schoolinout.common.utils.StringUtils;
+import com.schoolinout.framework.web.service.TokenService;
+import com.schoolinout.system.domain.ParentStudent;
+import com.schoolinout.system.domain.Student;
 import com.schoolinout.system.domain.dto.ApiParentDto;
-import com.schoolinout.system.service.IApiParentService;
 import com.schoolinout.system.service.ISysDictTypeService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.commons.lang3.RandomStringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
+import javax.annotation.Resource;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author sakura
@@ -24,20 +42,72 @@ import java.util.List;
 @RestController
 public class Api_ParentController {
 
+    public static final String CODE_CACHE_KEY = "validate:code:";
+
+    @Resource(name = "parentLoginAuthenticationManagerBean")
+    private AuthenticationManager authenticationManager;
+
     @Autowired
     private IApiParentService parentService;
+    @Autowired
+    private TokenService tokenService;
+    @Autowired
+    private IApiStudentService studentService;
+    @Autowired
+    private IApiParentStudentRelationService relationService;
 
     @Autowired
     private ISysDictTypeService dictTypeService;
 
+
+    @Autowired
+    private RedisCache redisCache;
+
     @Operation(summary = "家长登入")
     @PostMapping("/login")
     public AjaxResult parentLogin(@Validated @RequestBody ApiParentDto form) {
         // step1:校验验证码是否正确
+        boolean matches = form.getPhone().matches("^1[34578]\\d{9}$");
+        if (!matches) {
+            return AjaxResult.error("手机号输入格式不正确");
+        }
+        String cacheCode = redisCache.getCacheObject(form.getPhone());
+        if (cacheCode == null || !cacheCode.equalsIgnoreCase(form.getCode())) {
+            return AjaxResult.error("验证码输入错误");
+        }
         // step2:通过手机号去查询家长
+        Authentication authenticate;
+        try {
+            authenticate = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(form.getPhone(), form.getPhone()));
+        } catch (Exception e) {
+            if (e instanceof BadCredentialsException) {
+                throw new UserPasswordNotMatchException();
+            } else {
+                throw new ServiceException(e.getMessage());
+            }
+        }
         // step3:通过学号去查询学生
-        // step4:返回登入成功的令牌
-        return AjaxResult.success();
+        Student selectConditionStu = new Student();
+        selectConditionStu.setStudentNum(form.getStudentNum());
+        Student student = studentService.listStudentSimple(selectConditionStu);
+        if (student == null) {
+            return AjaxResult.error("当前学号不存在学生");
+        }
+        // step4:查询当前学号是否跟当前家长绑定
+        LoginUser principal = (LoginUser) authenticate.getPrincipal();
+        ParentLogin parentLogin = principal.getParentLogin();
+        ParentStudent parentStudent = relationService.listParentStudentSimple(parentLogin.getId(), student.getId());
+        if (parentStudent == null) {
+            return AjaxResult.success("当前家长未绑定有学号对应的学生", false);
+        }
+        // step5:返回登入成功的令牌
+        String token = tokenService.createToken(principal);
+        // 生成token
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("token", token);
+        map.put("user", parentLogin);
+        // TODO (sakura, 2024/1/10, 12:42, 数据脱敏)
+        return AjaxResult.success("登入成功", map);
     }
 
     @Operation(summary = "家长注册")
@@ -53,8 +123,17 @@ public class Api_ParentController {
     @Operation(summary = "发送手机号验证码")
     @PostMapping("/send/phone/code/{phone}")
     public AjaxResult sendPhoneCode(@PathVariable String phone) {
+        boolean matches = phone.matches("^1[34578]\\d{9}$");
+        if (!matches) {
+            return AjaxResult.error("手机号输入格式不正确");
+        }
+        // 拼 key
+        String cacheKey = CODE_CACHE_KEY + phone;
         // 发生验证码
-        return AjaxResult.success();
+        String code = RandomStringUtils.random(6, "0123456789");
+        // 5 分钟有效时间
+        redisCache.setCacheObject(cacheKey, code, 5, TimeUnit.MINUTES);
+        return AjaxResult.success("短信验证码已发送,请注意查收!");
     }
 
     @Operation(summary = "获取家长与学生关系列表")

+ 87 - 0
school-in-out-common/src/main/java/com/schoolinout/common/core/domain/entity/ParentLogin.java

@@ -0,0 +1,87 @@
+package com.schoolinout.common.core.domain.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 11:11:09 Wed
+ */
+public class ParentLogin {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * id
+     */
+    private Long id;
+
+    @TableField(exist = false)
+    private String studentIds;
+
+
+    /**
+     * 家长姓名
+     */
+    private String parentName;
+
+    /**
+     * 家长电话
+     */
+    private String parentPhone;
+
+    /**
+     * 与学生的关系
+     */
+    private String relation;
+
+    @Override
+    public String toString() {
+        return "ParentLogin{" +
+                "id=" + id +
+                ", studentIds='" + studentIds + '\'' +
+                ", parentName='" + parentName + '\'' +
+                ", parentPhone='" + parentPhone + '\'' +
+                ", relation='" + relation + '\'' +
+                '}';
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getStudentIds() {
+        return studentIds;
+    }
+
+    public void setStudentIds(String studentIds) {
+        this.studentIds = studentIds;
+    }
+
+    public String getParentName() {
+        return parentName;
+    }
+
+    public void setParentName(String parentName) {
+        this.parentName = parentName;
+    }
+
+    public String getParentPhone() {
+        return parentPhone;
+    }
+
+    public void setParentPhone(String parentPhone) {
+        this.parentPhone = parentPhone;
+    }
+
+    public String getRelation() {
+        return relation;
+    }
+
+    public void setRelation(String relation) {
+        this.relation = relation;
+    }
+}

+ 85 - 85
school-in-out-common/src/main/java/com/schoolinout/common/core/domain/entity/SysDept.java

@@ -1,203 +1,203 @@
 package com.schoolinout.common.core.domain.entity;
 
-import java.util.ArrayList;
-import java.util.List;
+import com.schoolinout.common.core.domain.BaseEntity;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
 import javax.validation.constraints.Email;
 import javax.validation.constraints.NotBlank;
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import com.schoolinout.common.core.domain.BaseEntity;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * 部门表 sys_dept
- * 
+ *
  * @author ruoyi
  */
-public class SysDept extends BaseEntity
-{
+public class SysDept extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
-    /** 部门ID */
+    /**
+     * 部门ID
+     */
     private Long deptId;
 
-    /** 父部门ID */
+    /**
+     * 父部门ID
+     */
     private Long parentId;
 
-    /** 祖级列表 */
+    /**
+     * 祖级列表
+     */
     private String ancestors;
 
-    /** 部门名称 */
+    /**
+     * 部门名称
+     */
     private String deptName;
 
-    /** 显示顺序 */
+    /**
+     * 显示顺序
+     */
     private Integer orderNum;
 
-    /** 负责人 */
+    /**
+     * 负责人
+     */
     private String leader;
 
-    /** 联系电话 */
+    /**
+     * 联系电话
+     */
     private String phone;
 
-    /** 邮箱 */
+    /**
+     * 邮箱
+     */
     private String email;
 
-    /** 部门状态:0正常,1停用 */
+    /**
+     * 部门状态:0正常,1停用
+     */
     private String status;
 
-    /** 删除标志(0代表存在 2代表删除) */
+    /**
+     * 删除标志(0代表存在 2代表删除)
+     */
     private String delFlag;
 
-    /** 父部门名称 */
+    /**
+     * 父部门名称
+     */
     private String parentName;
-    
-    /** 子部门 */
+
+    /**
+     * 子部门
+     */
     private List<SysDept> children = new ArrayList<SysDept>();
 
-    public Long getDeptId()
-    {
+    public Long getDeptId() {
         return deptId;
     }
 
-    public void setDeptId(Long deptId)
-    {
+    public void setDeptId(Long deptId) {
         this.deptId = deptId;
     }
 
-    public Long getParentId()
-    {
+    public Long getParentId() {
         return parentId;
     }
 
-    public void setParentId(Long parentId)
-    {
+    public void setParentId(Long parentId) {
         this.parentId = parentId;
     }
 
-    public String getAncestors()
-    {
+    public String getAncestors() {
         return ancestors;
     }
 
-    public void setAncestors(String ancestors)
-    {
+    public void setAncestors(String ancestors) {
         this.ancestors = ancestors;
     }
 
     @NotBlank(message = "部门名称不能为空")
     @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
-    public String getDeptName()
-    {
+    public String getDeptName() {
         return deptName;
     }
 
-    public void setDeptName(String deptName)
-    {
+    public void setDeptName(String deptName) {
         this.deptName = deptName;
     }
 
     @NotNull(message = "显示顺序不能为空")
-    public Integer getOrderNum()
-    {
+    public Integer getOrderNum() {
         return orderNum;
     }
 
-    public void setOrderNum(Integer orderNum)
-    {
+    public void setOrderNum(Integer orderNum) {
         this.orderNum = orderNum;
     }
 
-    public String getLeader()
-    {
+    public String getLeader() {
         return leader;
     }
 
-    public void setLeader(String leader)
-    {
+    public void setLeader(String leader) {
         this.leader = leader;
     }
 
     @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
-    public String getPhone()
-    {
+    public String getPhone() {
         return phone;
     }
 
-    public void setPhone(String phone)
-    {
+    public void setPhone(String phone) {
         this.phone = phone;
     }
 
     @Email(message = "邮箱格式不正确")
     @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
-    public String getEmail()
-    {
+    public String getEmail() {
         return email;
     }
 
-    public void setEmail(String email)
-    {
+    public void setEmail(String email) {
         this.email = email;
     }
 
-    public String getStatus()
-    {
+    public String getStatus() {
         return status;
     }
 
-    public void setStatus(String status)
-    {
+    public void setStatus(String status) {
         this.status = status;
     }
 
-    public String getDelFlag()
-    {
+    public String getDelFlag() {
         return delFlag;
     }
 
-    public void setDelFlag(String delFlag)
-    {
+    public void setDelFlag(String delFlag) {
         this.delFlag = delFlag;
     }
 
-    public String getParentName()
-    {
+    public String getParentName() {
         return parentName;
     }
 
-    public void setParentName(String parentName)
-    {
+    public void setParentName(String parentName) {
         this.parentName = parentName;
     }
 
-    public List<SysDept> getChildren()
-    {
+    public List<SysDept> getChildren() {
         return children;
     }
 
-    public void setChildren(List<SysDept> children)
-    {
+    public void setChildren(List<SysDept> children) {
         this.children = children;
     }
 
     @Override
     public String toString() {
-        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
-            .append("deptId", getDeptId())
-            .append("parentId", getParentId())
-            .append("ancestors", getAncestors())
-            .append("deptName", getDeptName())
-            .append("orderNum", getOrderNum())
-            .append("leader", getLeader())
-            .append("phone", getPhone())
-            .append("email", getEmail())
-            .append("status", getStatus())
-            .append("delFlag", getDelFlag())
-            .append("createBy", getCreateBy())
-            .append("createTime", getCreateTime())
-            .append("updateBy", getUpdateBy())
-            .append("updateTime", getUpdateTime())
-            .toString();
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("deptId", getDeptId())
+                .append("parentId", getParentId())
+                .append("ancestors", getAncestors())
+                .append("deptName", getDeptName())
+                .append("orderNum", getOrderNum())
+                .append("leader", getLeader())
+                .append("phone", getPhone())
+                .append("email", getEmail())
+                .append("status", getStatus())
+                .append("delFlag", getDelFlag())
+                .append("createBy", getCreateBy())
+                .append("createTime", getCreateTime())
+                .append("updateBy", getUpdateBy())
+                .append("updateTime", getUpdateTime())
+                .toString();
     }
 }

+ 63 - 72
school-in-out-common/src/main/java/com/schoolinout/common/core/domain/model/LoginUser.java

@@ -1,19 +1,20 @@
 package com.schoolinout.common.core.domain.model;
 
 import com.alibaba.fastjson2.annotation.JSONField;
+import com.schoolinout.common.core.domain.entity.ParentLogin;
 import com.schoolinout.common.core.domain.entity.SysUser;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.userdetails.UserDetails;
+
 import java.util.Collection;
 import java.util.Set;
 
 /**
  * 登录用户身份权限
- * 
+ *
  * @author ruoyi
  */
-public class LoginUser implements UserDetails
-{
+public class LoginUser implements UserDetails {
     private static final long serialVersionUID = 1L;
 
     /**
@@ -71,65 +72,76 @@ public class LoginUser implements UserDetails
      */
     private SysUser user;
 
-    public LoginUser()
-    {
+    /**
+     * 客户端家长登入
+     */
+    private ParentLogin parentLogin;
+
+    public LoginUser() {
+    }
+
+    public LoginUser(Long parentId, ParentLogin parentLogin) {
+        this.userId = parentId;
+        this.parentLogin = parentLogin;
     }
 
-    public LoginUser(SysUser user, Set<String> permissions)
-    {
+    public LoginUser(SysUser user, Set<String> permissions) {
         this.user = user;
         this.permissions = permissions;
     }
 
-    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions)
-    {
+    public LoginUser(Long userId, Long deptId, SysUser user, Set<String> permissions) {
         this.userId = userId;
         this.deptId = deptId;
         this.user = user;
         this.permissions = permissions;
     }
 
-    public Long getUserId()
-    {
+    public Long getUserId() {
         return userId;
     }
 
-    public void setUserId(Long userId)
-    {
+    public void setUserId(Long userId) {
         this.userId = userId;
     }
 
-    public Long getDeptId()
-    {
+    public Long getDeptId() {
         return deptId;
     }
 
-    public void setDeptId(Long deptId)
-    {
+    public void setDeptId(Long deptId) {
         this.deptId = deptId;
     }
 
-    public String getToken()
-    {
+    public String getToken() {
         return token;
     }
 
-    public void setToken(String token)
-    {
+    public void setToken(String token) {
         this.token = token;
     }
 
     @JSONField(serialize = false)
     @Override
-    public String getPassword()
-    {
-        return user.getPassword();
+    public String getPassword() {
+        if (user != null) {
+            return user.getPassword();
+        } else return parentLogin.getParentPhone();
     }
 
     @Override
-    public String getUsername()
-    {
-        return user.getUserName();
+    public String getUsername() {
+        if (user != null) {
+            return user.getUserName();
+        } else return parentLogin.getParentPhone();
+    }
+
+    public ParentLogin getParentLogin() {
+        return parentLogin;
+    }
+
+    public void setParentLogin(ParentLogin parentLogin) {
+        this.parentLogin = parentLogin;
     }
 
     /**
@@ -137,130 +149,109 @@ public class LoginUser implements UserDetails
      */
     @JSONField(serialize = false)
     @Override
-    public boolean isAccountNonExpired()
-    {
+    public boolean isAccountNonExpired() {
         return true;
     }
 
     /**
      * 指定用户是否解锁,锁定的用户无法进行身份验证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)
     @Override
-    public boolean isAccountNonLocked()
-    {
+    public boolean isAccountNonLocked() {
         return true;
     }
 
     /**
      * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)
     @Override
-    public boolean isCredentialsNonExpired()
-    {
+    public boolean isCredentialsNonExpired() {
         return true;
     }
 
     /**
      * 是否可用 ,禁用的用户不能身份验证
-     * 
+     *
      * @return
      */
     @JSONField(serialize = false)
     @Override
-    public boolean isEnabled()
-    {
+    public boolean isEnabled() {
         return true;
     }
 
-    public Long getLoginTime()
-    {
+    public Long getLoginTime() {
         return loginTime;
     }
 
-    public void setLoginTime(Long loginTime)
-    {
+    public void setLoginTime(Long loginTime) {
         this.loginTime = loginTime;
     }
 
-    public String getIpaddr()
-    {
+    public String getIpaddr() {
         return ipaddr;
     }
 
-    public void setIpaddr(String ipaddr)
-    {
+    public void setIpaddr(String ipaddr) {
         this.ipaddr = ipaddr;
     }
 
-    public String getLoginLocation()
-    {
+    public String getLoginLocation() {
         return loginLocation;
     }
 
-    public void setLoginLocation(String loginLocation)
-    {
+    public void setLoginLocation(String loginLocation) {
         this.loginLocation = loginLocation;
     }
 
-    public String getBrowser()
-    {
+    public String getBrowser() {
         return browser;
     }
 
-    public void setBrowser(String browser)
-    {
+    public void setBrowser(String browser) {
         this.browser = browser;
     }
 
-    public String getOs()
-    {
+    public String getOs() {
         return os;
     }
 
-    public void setOs(String os)
-    {
+    public void setOs(String os) {
         this.os = os;
     }
 
-    public Long getExpireTime()
-    {
+    public Long getExpireTime() {
         return expireTime;
     }
 
-    public void setExpireTime(Long expireTime)
-    {
+    public void setExpireTime(Long expireTime) {
         this.expireTime = expireTime;
     }
 
-    public Set<String> getPermissions()
-    {
+    public Set<String> getPermissions() {
         return permissions;
     }
 
-    public void setPermissions(Set<String> permissions)
-    {
+    public void setPermissions(Set<String> permissions) {
         this.permissions = permissions;
     }
 
-    public SysUser getUser()
-    {
+    public SysUser getUser() {
         return user;
     }
 
-    public void setUser(SysUser user)
-    {
+    public void setUser(SysUser user) {
         this.user = user;
     }
 
     @Override
-    public Collection<? extends GrantedAuthority> getAuthorities()
-    {
+    public Collection<? extends GrantedAuthority> getAuthorities() {
         return null;
     }
 }

+ 27 - 1
school-in-out-framework/src/main/java/com/schoolinout/framework/config/SecurityConfig.java

@@ -5,9 +5,13 @@ import com.schoolinout.framework.security.filter.JwtAuthenticationTokenFilter;
 import com.schoolinout.framework.security.handle.AuthenticationEntryPointImpl;
 import com.schoolinout.framework.security.handle.LogoutSuccessHandlerImpl;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
 import org.springframework.http.HttpMethod;
 import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -16,6 +20,7 @@ import org.springframework.security.config.annotation.web.configurers.Expression
 import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.NoOpPasswordEncoder;
 import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 import org.springframework.security.web.authentication.logout.LogoutFilter;
 import org.springframework.web.filter.CorsFilter;
@@ -63,6 +68,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
     @Autowired
     private PermitAllUrlProperties permitAllUrl;
 
+    @Autowired
+    @Qualifier("parentLoginService")
+    private UserDetailsService parentLoginService;
+
     /**
      * 解决 无法直接注入 AuthenticationManager
      *
@@ -70,11 +79,28 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
      * @throws Exception
      */
     @Bean
+    @Primary
     @Override
     public AuthenticationManager authenticationManagerBean() throws Exception {
         return super.authenticationManagerBean();
     }
 
+
+    /**
+     * 解决 无法直接注入 AuthenticationManager
+     *
+     * @return
+     * @throws Exception
+     */
+    @Bean("parentLoginAuthenticationManagerBean")
+    public AuthenticationManager parentLoginAuthenticationManagerBean() throws Exception {
+        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
+        authenticationProvider.setUserDetailsService(parentLoginService);
+        authenticationProvider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
+        return new ProviderManager(authenticationProvider);
+    }
+
+
     /**
      * anyRequest          |   匹配所有请求路径
      * access              |   SpringEl表达式结果为true时可以访问
@@ -110,7 +136,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
                 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
                 .antMatchers("/login", "/register", "/captchaImage").permitAll()
                 // 客户端登入放行路径
-                .antMatchers("/api/parent/login", "/api/parent/register", "api/parent/list/relation", "/api/parent/send/phone/code/**").permitAll()
+                .antMatchers("/app/parent/login", "/app/parent/register", "/app/parent/list/relation", "/app/parent/send/phone/code/**").permitAll()
                 // 静态资源,可匿名访问
                 .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
                 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/doc.html", "/v3/api-docs/**").permitAll()

+ 1 - 1
school-in-out-framework/src/main/java/com/schoolinout/framework/web/exception/GlobalExceptionHandler.java

@@ -95,7 +95,7 @@ public class GlobalExceptionHandler {
     public AjaxResult handleException(Exception e, HttpServletRequest request) {
         String requestURI = request.getRequestURI();
         log.error("请求地址'{}',发生系统异常.", requestURI, e);
-        return AjaxResult.error(e.getMessage());
+        return AjaxResult.error("系统异常");
     }
 
     /**

+ 48 - 0
school-in-out-framework/src/main/java/com/schoolinout/framework/web/service/ParentLoginUserDetailsServiceImpl.java

@@ -0,0 +1,48 @@
+package com.schoolinout.framework.web.service;
+
+import com.schoolinout.api.mapper.ApiParentMapper;
+import com.schoolinout.common.core.domain.entity.ParentLogin;
+import com.schoolinout.common.core.domain.model.LoginUser;
+import com.schoolinout.common.exception.ServiceException;
+import com.schoolinout.common.utils.StringUtils;
+import com.schoolinout.common.utils.bean.BeanUtils;
+import com.schoolinout.system.domain.Parent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 11:11:11 Wed
+ */
+@Service("parentLoginService")
+public class ParentLoginUserDetailsServiceImpl implements UserDetailsService {
+
+    private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
+
+    @Autowired
+    private ApiParentMapper parentMapper;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        Parent selectCondition = new Parent();
+        selectCondition.setParentPhone(username);
+        Parent parent = parentMapper.selectOnlyOne(selectCondition);//验证登录用户,查询数据库,如果这个mapper定义在自己的模块,引入maven依赖不用我多说吧?
+        if (StringUtils.isNull(parent)) {
+            log.info("登录用户:{} 不存在.", username);
+            throw new ServiceException("登录用户:" + username + " 不存在");
+        }
+        log.info("客户端家长登入:姓名:{}, 电话:{}", parent.getParentName(), parent.getParentPhone());
+        return createLoginUser(parent);
+    }
+
+    public UserDetails createLoginUser(Parent parent) {
+        ParentLogin parentLogin = new ParentLogin();
+        BeanUtils.copyBeanProp(parentLogin, parent);
+        return new LoginUser(parent.getId(), parentLogin);
+    }
+}

+ 16 - 22
school-in-out-framework/src/main/java/com/schoolinout/framework/web/service/UserDetailsServiceImpl.java

@@ -1,12 +1,5 @@
 package com.schoolinout.framework.web.service;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.userdetails.UserDetails;
-import org.springframework.security.core.userdetails.UserDetailsService;
-import org.springframework.security.core.userdetails.UsernameNotFoundException;
-import org.springframework.stereotype.Service;
 import com.schoolinout.common.core.domain.entity.SysUser;
 import com.schoolinout.common.core.domain.model.LoginUser;
 import com.schoolinout.common.enums.UserStatus;
@@ -14,6 +7,14 @@ import com.schoolinout.common.exception.ServiceException;
 import com.schoolinout.common.utils.MessageUtils;
 import com.schoolinout.common.utils.StringUtils;
 import com.schoolinout.system.service.ISysUserService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Primary;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
 
 /**
  * 用户验证处理
@@ -21,13 +22,13 @@ import com.schoolinout.system.service.ISysUserService;
  * @author ruoyi
  */
 @Service
-public class UserDetailsServiceImpl implements UserDetailsService
-{
+@Primary
+public class UserDetailsServiceImpl implements UserDetailsService {
     private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
 
     @Autowired
     private ISysUserService userService;
-    
+
     @Autowired
     private SysPasswordService passwordService;
 
@@ -35,21 +36,15 @@ public class UserDetailsServiceImpl implements UserDetailsService
     private SysPermissionService permissionService;
 
     @Override
-    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
-    {
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
         SysUser user = userService.selectUserByUserName(username);
-        if (StringUtils.isNull(user))
-        {
+        if (StringUtils.isNull(user)) {
             log.info("登录用户:{} 不存在.", username);
             throw new ServiceException(MessageUtils.message("user.not.exists"));
-        }
-        else if (UserStatus.DELETED.getCode().equals(user.getDelFlag()))
-        {
+        } else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
             log.info("登录用户:{} 已被删除.", username);
             throw new ServiceException(MessageUtils.message("user.password.delete"));
-        }
-        else if (UserStatus.DISABLE.getCode().equals(user.getStatus()))
-        {
+        } else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
             log.info("登录用户:{} 已被停用.", username);
             throw new ServiceException(MessageUtils.message("user.blocked"));
         }
@@ -59,8 +54,7 @@ public class UserDetailsServiceImpl implements UserDetailsService
         return createLoginUser(user);
     }
 
-    public UserDetails createLoginUser(SysUser user)
-    {
+    public UserDetails createLoginUser(SysUser user) {
         return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user));
     }
 }

+ 14 - 0
school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiParentMapper.java

@@ -0,0 +1,14 @@
+package com.schoolinout.api.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.schoolinout.system.domain.Parent;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 09:9:58 Wed
+ */
+@Mapper
+public interface ApiParentMapper extends BaseMapper<Parent> {
+    Parent selectOnlyOne(Parent selectCondition);
+}

+ 15 - 0
school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiParentStudentRelationMapper.java

@@ -0,0 +1,15 @@
+package com.schoolinout.api.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.schoolinout.system.domain.ParentStudent;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:50 Wed
+ */
+@Mapper
+public interface ApiParentStudentRelationMapper extends BaseMapper<ParentStudent> {
+    ParentStudent selectOnlyOne(@Param("parentId") Long parentId, @Param("studentId") Long studentId);
+}

+ 14 - 0
school-in-out-system/src/main/java/com/schoolinout/api/mapper/ApiStudentMapper.java

@@ -0,0 +1,14 @@
+package com.schoolinout.api.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.schoolinout.system.domain.Student;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:11 Wed
+ */
+@Mapper
+public interface ApiStudentMapper extends BaseMapper<Student> {
+    Student selectOnlyOne(Student student);
+}

+ 17 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/IApiParentService.java

@@ -0,0 +1,17 @@
+package com.schoolinout.api.service;
+
+import com.schoolinout.system.domain.Parent;
+
+/**
+ * @author sakura
+ * @date 2024/1/9 17:17:18 Tue
+ */
+public interface IApiParentService {
+    /**
+     * 搜索单个家长对象
+     *
+     * @param selectCondition 查询条件
+     * @return 结果
+     */
+    public Parent listParentSimple(Parent selectCondition);
+}

+ 12 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/IApiParentStudentRelationService.java

@@ -0,0 +1,12 @@
+package com.schoolinout.api.service;
+
+import com.schoolinout.system.domain.ParentStudent;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:48 Wed
+ */
+public interface IApiParentStudentRelationService {
+
+    public ParentStudent listParentStudentSimple(Long parentId, Long studentId);
+}

+ 18 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/IApiStudentService.java

@@ -0,0 +1,18 @@
+package com.schoolinout.api.service;
+
+import com.schoolinout.system.domain.Student;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:05 Wed
+ */
+public interface IApiStudentService {
+    /**
+     * 查询单个用户
+     *
+     * @param selectConditionStu 查询条件
+     * @return 结果
+     */
+    public Student listStudentSimple(Student selectConditionStu);
+
+}

+ 23 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiParentServiceImpl.java

@@ -0,0 +1,23 @@
+package com.schoolinout.api.service.impl;
+
+import com.schoolinout.api.mapper.ApiParentMapper;
+import com.schoolinout.api.service.IApiParentService;
+import com.schoolinout.system.domain.Parent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author sakura
+ * @date 2024/1/9 17:17:18 Tue
+ */
+@Service
+public class ApiParentServiceImpl implements IApiParentService {
+
+    @Autowired
+    private ApiParentMapper parentMapper;
+
+    @Override
+    public Parent listParentSimple(Parent selectCondition) {
+        return parentMapper.selectOnlyOne(selectCondition);
+    }
+}

+ 23 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiParentStudentRelationServiceImpl.java

@@ -0,0 +1,23 @@
+package com.schoolinout.api.service.impl;
+
+import com.schoolinout.api.mapper.ApiParentStudentRelationMapper;
+import com.schoolinout.api.service.IApiParentStudentRelationService;
+import com.schoolinout.system.domain.ParentStudent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:49 Wed
+ */
+@Service
+public class ApiParentStudentRelationServiceImpl implements IApiParentStudentRelationService {
+
+    @Autowired
+    private ApiParentStudentRelationMapper relationMapper;
+
+    @Override
+    public ParentStudent listParentStudentSimple(Long parentId, Long studentId) {
+        return relationMapper.selectOnlyOne(parentId, studentId);
+    }
+}

+ 23 - 0
school-in-out-system/src/main/java/com/schoolinout/api/service/impl/ApiStudentServiceImpl.java

@@ -0,0 +1,23 @@
+package com.schoolinout.api.service.impl;
+
+import com.schoolinout.api.mapper.ApiStudentMapper;
+import com.schoolinout.api.service.IApiStudentService;
+import com.schoolinout.system.domain.Student;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author sakura
+ * @date 2024/1/10 10:10:06 Wed
+ */
+@Service
+public class ApiStudentServiceImpl implements IApiStudentService {
+
+    @Autowired
+    private ApiStudentMapper studentMapper;
+
+    @Override
+    public Student listStudentSimple(Student selectConditionStu) {
+        return studentMapper.selectOnlyOne(selectConditionStu);
+    }
+}

+ 0 - 8
school-in-out-system/src/main/java/com/schoolinout/system/service/IApiParentService.java

@@ -1,8 +0,0 @@
-package com.schoolinout.system.service;
-
-/**
- * @author sakura
- * @date 2024/1/9 17:17:18 Tue
- */
-public interface IApiParentService {
-}

+ 0 - 12
school-in-out-system/src/main/java/com/schoolinout/system/service/impl/ApiParentServiceImpl.java

@@ -1,12 +0,0 @@
-package com.schoolinout.system.service.impl;
-
-import com.schoolinout.system.service.IApiParentService;
-import org.springframework.stereotype.Service;
-
-/**
- * @author sakura
- * @date 2024/1/9 17:17:18 Tue
- */
-@Service
-public class ApiParentServiceImpl implements IApiParentService {
-}

+ 32 - 0
school-in-out-system/src/main/resources/mapper/api/ApiParentMapper.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.schoolinout.api.mapper.ApiParentMapper">
+    <select id="selectOnlyOne" resultType="com.schoolinout.system.domain.Parent">
+        SELECT
+        p.id,
+        p.parent_name,
+        p.parent_phone,
+        p.relation,
+        p.del_flag,
+        p.create_by,
+        p.create_time,
+        p.update_by,
+        p.update_time,
+        GROUP_CONCAT(sp.student_id SEPARATOR ',') AS student_ids
+        from tb_school_parent p
+        LEFT JOIN tb_school_student_parent_relation sp ON p.id = sp.parent_id
+        <where>
+            <if test="id != null">
+                AND p.id = #{id}
+            </if>
+            <if test="parentPhone != null">
+                AND p.parent_phone = #{parentPhone}
+            </if>
+            <if test="parentName != null">
+                AND p.parent_name LIKE '%' #{parentName} '%'
+            </if>
+        </where>
+    </select>
+</mapper>

+ 13 - 0
school-in-out-system/src/main/resources/mapper/api/ApiParentStudentRelationMapper.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.schoolinout.api.mapper.ApiParentStudentRelationMapper">
+    <select id="selectOnlyOne" resultType="com.schoolinout.system.domain.ParentStudent">
+        SELECT parent_id,
+               student_id
+        from tb_school_student_parent_relation
+        where parent_id = #{parentId}
+          and student_id = #{studentId}
+    </select>
+</mapper>

+ 26 - 0
school-in-out-system/src/main/resources/mapper/api/ApiStudentMapper.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.schoolinout.api.mapper.ApiStudentMapper">
+    <select id="selectOnlyOne" resultType="com.schoolinout.system.domain.Student">
+        SELECT
+        id, school_id, class_id, student_name, student_pic, student_gender, student_num, del_flag, create_by,
+        create_time, update_by, update_time
+        FROM tb_school_student
+        <where>
+            <if test="id != null">
+                AND id = #{id}
+            </if>
+            <if test="schoolId!=null">
+                AND school_id = #{schoolId}
+            </if>
+            <if test="classId!=null">
+                AND class_id = #{classId}
+            </if>
+            <if test="studentNum != null">
+                AND student_num = #{studentNum}
+            </if>
+        </where>
+    </select>
+</mapper>

+ 5 - 0
school-in-out-ui/src/views/system/teacher/index.vue

@@ -325,6 +325,11 @@ export default {
 						message: '联系电话不能为空',
 						trigger: 'blur',
 					},
+					{
+						pattern: /^1[34578]\d{9}$/,
+						message: '手机号码格式错误',
+						trigger: 'blur',
+					}
 				],
 			},
 			dictTables: {