|
|
@@ -1,996 +1,1024 @@
|
|
|
-package com.zxyj.service;
|
|
|
-
|
|
|
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
-import com.zxyj.config.JwtConfig;
|
|
|
-import com.zxyj.dto.*;
|
|
|
-import com.zxyj.dto.RegisterWithIdCardDTO;
|
|
|
-import com.zxyj.dto.RegisterWithInviteCodeDTO;
|
|
|
-import com.zxyj.entity.Child;
|
|
|
-import com.zxyj.entity.Family;
|
|
|
-import com.zxyj.entity.User;
|
|
|
-import com.zxyj.mapper.ChildMapper;
|
|
|
-import com.zxyj.mapper.FamilyMapper;
|
|
|
-import com.zxyj.mapper.UserMapper;
|
|
|
-import com.zxyj.service.VerificationCodeService;
|
|
|
-import com.zxyj.service.WechatService;
|
|
|
-import com.zxyj.service.api.UserServiceInterface;
|
|
|
-import javax.annotation.Resource;
|
|
|
-import org.springframework.stereotype.Service;
|
|
|
-import org.springframework.util.DigestUtils;
|
|
|
-
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-import java.util.*;
|
|
|
-
|
|
|
-
|
|
|
-@Service
|
|
|
-public class UserService implements UserServiceInterface {
|
|
|
-
|
|
|
-@Resource
|
|
|
-private UserMapper userMapper;
|
|
|
-
|
|
|
-@Resource
|
|
|
-private FamilyMapper familyMapper;
|
|
|
-
|
|
|
-@Resource
|
|
|
-private ChildMapper childMapper;
|
|
|
-
|
|
|
-@Resource
|
|
|
-private JwtConfig jwtConfig;
|
|
|
-
|
|
|
-@Resource
|
|
|
-private VerificationCodeService verificationCodeService;
|
|
|
-
|
|
|
-@Resource
|
|
|
-private WechatService wechatService;
|
|
|
-
|
|
|
- public LoginResultDTO wechatLogin(WechatLoginDTO dto) {
|
|
|
- // 模拟微信登录,实际需要调用微信API获取openid
|
|
|
- String openid = "mock_openid_" + dto.getCode();
|
|
|
-
|
|
|
- // 查询用户是否存在
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getOpenid, openid));
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- // 新用户,创建家庭和用户
|
|
|
- Family family = new Family();
|
|
|
- family.setName("我的家庭");
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
-
|
|
|
- user = new User();
|
|
|
- user.setOpenid(openid);
|
|
|
- user.setUnionid("");
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setRole("parent");
|
|
|
- user.setNickname(dto.getNickname());
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- user.setPassword("");
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 手机号验证码登录
|
|
|
- */
|
|
|
- public LoginResultDTO phoneLogin(PhoneLoginDTO dto) {
|
|
|
- // 使用手机号作为唯一标识
|
|
|
- String phone = dto.getPhone();
|
|
|
-
|
|
|
- // 先通过手机号查找用户(更准确的判断)
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getPhone, phone));
|
|
|
-
|
|
|
- boolean isNewUser = false;
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- // 新用户,创建新家庭和用户
|
|
|
- isNewUser = true;
|
|
|
-
|
|
|
- Family family = new Family();
|
|
|
- family.setName("我的家庭");
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
-
|
|
|
- user = new User();
|
|
|
- user.setOpenid("phone:" + phone);
|
|
|
- user.setUnionid("");
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- // 默认角色为家长,可以被覆盖
|
|
|
- user.setRole(dto.getRole() != null ? dto.getRole() : "parent");
|
|
|
- user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7));
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- user.setPhone(phone);
|
|
|
- user.setBirthday(dto.getBirthday());
|
|
|
- user.setGender(dto.getGender());
|
|
|
- user.setPassword("");
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
- } else {
|
|
|
- // 老用户,更新信息(如果提供了)
|
|
|
- if (dto.getNickname() != null && !dto.getNickname().isEmpty()) {
|
|
|
- user.setNickname(dto.getNickname());
|
|
|
- }
|
|
|
- if (dto.getAvatar() != null && !dto.getAvatar().isEmpty()) {
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- }
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(isNewUser);
|
|
|
- result.setNeedsRoleSelect(isNewUser);
|
|
|
-
|
|
|
- return result;
|
|
|
-}
|
|
|
-
|
|
|
- /**
|
|
|
- * 微信手机号登录(正确获取openid和手机号)
|
|
|
- */
|
|
|
- public LoginResultDTO wechatPhoneLogin(WechatPhoneLoginDTO dto) {
|
|
|
- // 1. 通过code获取openid
|
|
|
- Map<String, String> sessionData = wechatService.code2Session(dto.getCode());
|
|
|
- String openid = sessionData.get("openid");
|
|
|
- String unionid = sessionData.get("unionid");
|
|
|
-
|
|
|
- // 2. 通过手机号code获取真实手机号
|
|
|
- String phone = wechatService.getPhoneNumber(dto.getPhoneCode());
|
|
|
-
|
|
|
- // 3. 先通过openid查找用户
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getOpenid, openid));
|
|
|
-
|
|
|
- boolean isNewUser = false;
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- // 4. openid不存在,通过手机号查找
|
|
|
- user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getPhone, phone));
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- // 5. 全新用户,创建新家庭和用户
|
|
|
- isNewUser = true;
|
|
|
-
|
|
|
- Family family = new Family();
|
|
|
- family.setName("我的家庭");
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
-
|
|
|
- user = new User();
|
|
|
- user.setOpenid(openid);
|
|
|
- user.setUnionid(unionid);
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setRole("parent");
|
|
|
- user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7));
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- user.setPhone(phone);
|
|
|
- user.setPassword("");
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
- } else {
|
|
|
- // 6. 手机号已存在但openid不同,绑定openid
|
|
|
- user.setOpenid(openid);
|
|
|
- user.setUnionid(unionid);
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 7. openid已存在,更新手机号(如果手机号变了)
|
|
|
- if (phone != null && !phone.equals(user.getPhone())) {
|
|
|
- user.setPhone(phone);
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(isNewUser);
|
|
|
- result.setOpenid(openid);
|
|
|
-
|
|
|
- // 新用户需要完善信息(仅当需要选择角色时)
|
|
|
- result.setNeedsRoleSelect(isNewUser);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 微信openid静默登录(用户再次打开小程序时自动登录)
|
|
|
- */
|
|
|
- public LoginResultDTO silentLogin(String code) {
|
|
|
- // 1. 通过code获取openid
|
|
|
- Map<String, String> sessionData = wechatService.code2Session(code);
|
|
|
- String openid = sessionData.get("openid");
|
|
|
- String unionid = sessionData.get("unionid");
|
|
|
-
|
|
|
- // 2. 查找用户
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getOpenid, openid));
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- throw new RuntimeException("用户不存在,请先登录");
|
|
|
- }
|
|
|
-
|
|
|
- // 3. 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(false);
|
|
|
- result.setOpenid(openid);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 通过openid自动登录(前端已缓存openid时调用)
|
|
|
- */
|
|
|
- public LoginResultDTO autoLoginByOpenid(String openid) {
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getOpenid, openid));
|
|
|
-
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(false);
|
|
|
- result.setOpenid(openid);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean setPassword(Long userId, String password) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- user.setPassword(encryptPassword(password));
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- public boolean verifyPassword(Long userId, String password) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null || user.getPassword() == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return user.getPassword().equals(encryptPassword(password));
|
|
|
- }
|
|
|
-
|
|
|
- public User getUserInfo(Long userId) {
|
|
|
- return userMapper.selectById(userId);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查手机号是否已注册
|
|
|
- * @param phone 手机号
|
|
|
- * @return true=已注册,false=未注册
|
|
|
- */
|
|
|
- public boolean isPhoneRegistered(String phone) {
|
|
|
- if (phone == null || phone.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getPhone, phone));
|
|
|
- return user != null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 直接注册(测试环境:不验证验证码)
|
|
|
- */
|
|
|
- public LoginResultDTO directRegister(DirectRegisterDTO dto) {
|
|
|
- // 创建家庭
|
|
|
- Family family = new Family();
|
|
|
- family.setName("我的家庭");
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
-
|
|
|
- // 创建用户
|
|
|
- User user = new User();
|
|
|
- user.setOpenid("phone:" + dto.getPhone());
|
|
|
- user.setUnionid("");
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setRole(dto.getRole() != null ? dto.getRole() : "parent");
|
|
|
- user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + dto.getPhone().substring(7));
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- user.setPhone(dto.getPhone());
|
|
|
- user.setBirthday(dto.getBirthday());
|
|
|
- user.setGender(dto.getGender());
|
|
|
- user.setPassword("");
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
-
|
|
|
- // 如果有邀请码,加入家庭
|
|
|
- if (dto.getInviteCode() != null && !dto.getInviteCode().isEmpty()) {
|
|
|
- try {
|
|
|
- joinFamily(user.getId(), dto.getInviteCode());
|
|
|
- } catch (Exception e) {
|
|
|
- // 忽略邀请码错误,继续使用创建的家庭
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(true);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public String switchMode(Long userId, String mode) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- // 验证用户是否拥有该角色
|
|
|
- if (!hasRole(user, mode)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- user.setRole(mode);
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return mode;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 切换用户当前角色 (支持 parent/child/teacher)
|
|
|
- * 用户需要先拥有该角色才能切换
|
|
|
- */
|
|
|
- public String switchRole(Long userId, String targetRole) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- // 验证用户是否拥有该角色
|
|
|
- if (!hasRole(user, targetRole)) {
|
|
|
- throw new RuntimeException("您没有该角色权限");
|
|
|
- }
|
|
|
- // 检查目标角色是否有效
|
|
|
- if (!isValidRole(targetRole)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- user.setRole(targetRole);
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return targetRole;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取用户所有角色列表
|
|
|
- */
|
|
|
- public List<String> getUserRoles(Long userId) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return new ArrayList<>();
|
|
|
- }
|
|
|
- return parseRoles(user.getRoles());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查用户是否拥有指定角色
|
|
|
- */
|
|
|
- public boolean hasRole(Long userId, String role) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return hasRole(user, role);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 添加角色到用户 (用于用户注册成为老师时)
|
|
|
- */
|
|
|
- public boolean addRole(Long userId, String newRole) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null || !isValidRole(newRole)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // 如果用户还没有roles字段,设置默认值
|
|
|
- if (user.getRoles() == null || user.getRoles().isEmpty()) {
|
|
|
- user.setRoles(user.getRole()); // 当前角色作为第一个角色
|
|
|
- }
|
|
|
- // 检查是否已拥有该角色
|
|
|
- if (hasRole(user, newRole)) {
|
|
|
- return true; // 已有该角色
|
|
|
- }
|
|
|
- // 添加新角色
|
|
|
- List<String> roleList = parseRoles(user.getRoles());
|
|
|
- roleList.add(newRole);
|
|
|
- user.setRoles(joinRoles(roleList));
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查用户是否拥有指定角色 (内部方法)
|
|
|
- */
|
|
|
- private boolean hasRole(User user, String role) {
|
|
|
- List<String> roles = parseRoles(user.getRoles());
|
|
|
- return roles.contains(role);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 解析角色字符串为列表
|
|
|
- */
|
|
|
- private List<String> parseRoles(String rolesStr) {
|
|
|
- List<String> result = new ArrayList<>();
|
|
|
- if (rolesStr == null || rolesStr.isEmpty()) {
|
|
|
- return result;
|
|
|
- }
|
|
|
- // 支持JSON数组格式或逗号分隔格式
|
|
|
- if (rolesStr.startsWith("[")) {
|
|
|
- // JSON格式解析
|
|
|
- rolesStr = rolesStr.replace("[", "").replace("]", "")
|
|
|
- .replace("\"", "").replace(" ", "");
|
|
|
- String[] arr = rolesStr.split(",");
|
|
|
- for (String r : arr) {
|
|
|
- if (!r.isEmpty()) {
|
|
|
- result.add(r.trim());
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // 逗号分隔格式
|
|
|
- String[] arr = rolesStr.split(",");
|
|
|
- for (String r : arr) {
|
|
|
- if (!r.trim().isEmpty()) {
|
|
|
- result.add(r.trim());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 将角色列表转换为字符串
|
|
|
- */
|
|
|
- private String joinRoles(List<String> roles) {
|
|
|
- return String.join(",", roles);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查角色是否有效
|
|
|
- */
|
|
|
- private boolean isValidRole(String role) {
|
|
|
- return "parent".equals(role) || "child".equals(role) || "teacher".equals(role);
|
|
|
- }
|
|
|
-
|
|
|
-/**
|
|
|
- * 家长切换到指定孩子身份(视图切换,不修改用户的实际角色)
|
|
|
- * 家长可以在家长模式下查看孩子的任务和积分
|
|
|
- */
|
|
|
- public Long switchToChild(Long userId, Long childId) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- Child child = childMapper.selectById(childId);
|
|
|
- if (child == null || !child.getFamilyId().equals(user.getFamilyId())) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // 注意:不修改用户的 role 字段,只是返回 childId 让前端切换视图
|
|
|
- // 前端应该使用 currentRole 或 viewMode 来控制显示模式
|
|
|
- return childId;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 从孩子视图切换回家长视图
|
|
|
- * 这只是确认切换操作,不需要修改数据库
|
|
|
- */
|
|
|
- public boolean switchBackToParent(Long userId) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // 不需要修改用户的 role 字段
|
|
|
- // 只是确认操作成功,前端切换视图
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- public LoginResultDTO registerWithIdCard(RegisterWithIdCardDTO dto) {
|
|
|
- // 验证验证码
|
|
|
- boolean valid = verificationCodeService.verifyCode(dto.getPhone(), dto.getCode(), "login");
|
|
|
- if (!valid) {
|
|
|
- throw new RuntimeException("验证码错误或已过期");
|
|
|
- }
|
|
|
-
|
|
|
- // 通过身份证查找是否已有注册的用户
|
|
|
- User existingUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getIdCard, dto.getIdCard()));
|
|
|
-
|
|
|
- Family family;
|
|
|
- User user;
|
|
|
-
|
|
|
- if (existingUser != null) {
|
|
|
- // 如果存在相同身份证的用户,加入其家庭
|
|
|
- user = existingUser;
|
|
|
- family = familyMapper.selectById(user.getFamilyId());
|
|
|
-
|
|
|
- // 更新用户信息(昵称和头像可以更新)
|
|
|
- user.setNickname(dto.getNickname());
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- // 手机号也可以更新(如果需要)
|
|
|
- user.setOpenid("phone:" + dto.getPhone());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- } else {
|
|
|
- // 创建新家庭和用户
|
|
|
- family = new Family();
|
|
|
- family.setName("我的家庭");
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
-
|
|
|
- user = new User();
|
|
|
- user.setOpenid("phone:" + dto.getPhone());
|
|
|
- user.setUnionid("");
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setRole("parent");
|
|
|
- user.setNickname(dto.getNickname());
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- user.setIdCard(dto.getIdCard());
|
|
|
- user.setRealName(dto.getRealName());
|
|
|
- user.setPassword(""); // 初始密码为空,需要后续设置
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- public ChildInfoDTO createChild(Long userId, CreateChildDTO dto) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (dto.getNickname() == null || dto.getNickname().trim().isEmpty()) {
|
|
|
- throw new IllegalArgumentException("孩子昵称不能为空");
|
|
|
- }
|
|
|
- if (dto.getAge() == null || dto.getAge() < 1 || dto.getAge() > 18) {
|
|
|
- throw new IllegalArgumentException("请输入有效的年龄(1-18岁)");
|
|
|
- }
|
|
|
-
|
|
|
- Child child = new Child();
|
|
|
- child.setUserId(userId);
|
|
|
- child.setFamilyId(user.getFamilyId());
|
|
|
- child.setNickname(dto.getNickname().trim());
|
|
|
- child.setAge(dto.getAge());
|
|
|
- child.setBirthday(dto.getBirthday());
|
|
|
- child.setGender(dto.getGender());
|
|
|
- child.setDanLevel(dto.getDanLevel());
|
|
|
- child.setPhone(dto.getPhone());
|
|
|
- child.setIdCard(dto.getIdCard());
|
|
|
- // 扣分开关:优先使用传入值,否则根据年龄判断
|
|
|
- if (dto.getPenaltyEnabled() != null) {
|
|
|
- child.setPenaltyEnabled(dto.getPenaltyEnabled());
|
|
|
- } else {
|
|
|
- // 年龄可能为空,默认不扣分
|
|
|
- Integer age = dto.getAge();
|
|
|
- child.setPenaltyEnabled(age != null && age >= 6 ? 1 : 0);
|
|
|
- }
|
|
|
- child.setTheme("default");
|
|
|
- child.setTotalPoints(0);
|
|
|
- child.setStreakDays(0);
|
|
|
- child.setFocusMaxDaily(3);
|
|
|
- child.setFocusRemaining(3);
|
|
|
- child.setFocusResetDate(new Date());
|
|
|
- child.setCreatedAt(new Date());
|
|
|
- child.setUpdatedAt(new Date());
|
|
|
- childMapper.insert(child);
|
|
|
-
|
|
|
- return toChildInfoDTO(child);
|
|
|
- }
|
|
|
-
|
|
|
- public List<ChildInfoDTO> getChildren(Long userId) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- List<Child> children = childMapper.selectList(new LambdaQueryWrapper<Child>()
|
|
|
- .eq(Child::getFamilyId, user.getFamilyId()));
|
|
|
-
|
|
|
- return children.stream().map(this::toChildInfoDTO).collect(java.util.stream.Collectors.toList());
|
|
|
- }
|
|
|
-
|
|
|
- public boolean updateChild(Long userId, Long childId, UpdateChildDTO dto) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- Child child = childMapper.selectById(childId);
|
|
|
- if (child == null || child.getFamilyId() == null || !child.getFamilyId().equals(user.getFamilyId())) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (dto.getNickname() != null) {
|
|
|
- child.setNickname(dto.getNickname());
|
|
|
- }
|
|
|
- if (dto.getAge() != null) {
|
|
|
- child.setAge(dto.getAge());
|
|
|
- }
|
|
|
- if (dto.getBirthday() != null) {
|
|
|
- child.setBirthday(dto.getBirthday());
|
|
|
- }
|
|
|
- if (dto.getGender() != null) {
|
|
|
- child.setGender(dto.getGender());
|
|
|
- }
|
|
|
- if (dto.getDanLevel() != null) {
|
|
|
- child.setDanLevel(dto.getDanLevel());
|
|
|
- }
|
|
|
- if (dto.getPhone() != null) {
|
|
|
- child.setPhone(dto.getPhone());
|
|
|
- }
|
|
|
- if (dto.getIdCard() != null) {
|
|
|
- child.setIdCard(dto.getIdCard());
|
|
|
- }
|
|
|
- if (dto.getPenaltyEnabled() != null) {
|
|
|
- child.setPenaltyEnabled(dto.getPenaltyEnabled());
|
|
|
- }
|
|
|
- if (dto.getTheme() != null) {
|
|
|
- child.setTheme(dto.getTheme());
|
|
|
- }
|
|
|
- child.setUpdatedAt(new Date());
|
|
|
- childMapper.updateById(child);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private String encryptPassword(String password) {
|
|
|
- return DigestUtils.md5DigestAsHex(("xzyj_salt_" + password).getBytes(StandardCharsets.UTF_8));
|
|
|
- }
|
|
|
-
|
|
|
- private String generateInviteCode() {
|
|
|
- return UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
|
|
- }
|
|
|
-
|
|
|
- private ChildInfoDTO toChildInfoDTO(Child child) {
|
|
|
- ChildInfoDTO dto = new ChildInfoDTO();
|
|
|
- dto.setId(child.getId());
|
|
|
- dto.setNickname(child.getNickname());
|
|
|
- dto.setAge(child.getAge());
|
|
|
- dto.setBirthday(child.getBirthday());
|
|
|
- dto.setGender(child.getGender());
|
|
|
- dto.setDanLevel(child.getDanLevel());
|
|
|
- dto.setPhone(child.getPhone());
|
|
|
- dto.setIdCard(child.getIdCard());
|
|
|
- dto.setPenaltyEnabled(child.getPenaltyEnabled());
|
|
|
- dto.setTheme(child.getTheme());
|
|
|
- dto.setTotalPoints(child.getTotalPoints());
|
|
|
- dto.setStreakDays(child.getStreakDays());
|
|
|
- dto.setFocusRemaining(child.getFocusRemaining());
|
|
|
- return dto;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 更新用户信息
|
|
|
- */
|
|
|
- public boolean updateUserInfo(Long userId, UpdateUserDTO dto) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- if (dto.getNickname() != null) {
|
|
|
- user.setNickname(dto.getNickname());
|
|
|
- }
|
|
|
- if (dto.getAvatar() != null) {
|
|
|
- user.setAvatar(dto.getAvatar());
|
|
|
- }
|
|
|
- if (dto.getRealName() != null) {
|
|
|
- user.setRealName(dto.getRealName());
|
|
|
- }
|
|
|
- // 更新角色
|
|
|
- if (dto.getRole() != null) {
|
|
|
- user.setRole(dto.getRole());
|
|
|
- }
|
|
|
- // 更新性别
|
|
|
- if (dto.getGender() != null) {
|
|
|
- user.setGender(dto.getGender());
|
|
|
- }
|
|
|
- // 更新生日(将字符串转换为Date)
|
|
|
- if (dto.getBirthday() != null && !dto.getBirthday().isEmpty()) {
|
|
|
- try {
|
|
|
- java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
- user.setBirthday(sdf.parse(dto.getBirthday()));
|
|
|
- } catch (Exception e) {
|
|
|
- // 忽略解析错误
|
|
|
- }
|
|
|
- }
|
|
|
- // 更新身份证号
|
|
|
- if (dto.getIdCard() != null && !dto.getIdCard().isEmpty()) {
|
|
|
- user.setIdCard(dto.getIdCard());
|
|
|
- // 如果没有手动设置生日,尝试从身份证号提取
|
|
|
- if (user.getBirthday() == null) {
|
|
|
- String birthdayFromIdCard = extractBirthdayFromIdCard(dto.getIdCard());
|
|
|
- if (birthdayFromIdCard != null) {
|
|
|
- try {
|
|
|
- java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
- user.setBirthday(sdf.parse(birthdayFromIdCard));
|
|
|
- } catch (Exception e) {
|
|
|
- // 忽略解析错误
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // 更新成长规划师信息
|
|
|
- if (dto.getTeacherNo() != null) {
|
|
|
- user.setTeacherNo(dto.getTeacherNo());
|
|
|
- }
|
|
|
- if (dto.getTeacherPhoto() != null) {
|
|
|
- user.setTeacherPhoto(dto.getTeacherPhoto());
|
|
|
- }
|
|
|
- if (dto.getCertificateImage() != null) {
|
|
|
- user.setCertificateImage(dto.getCertificateImage());
|
|
|
- }
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 从身份证号提取生日
|
|
|
- * 15位:1985年07月08日 -> 1985-07-08
|
|
|
- * 18位:1985年07月08日 -> 1985-07-08
|
|
|
- */
|
|
|
- private String extractBirthdayFromIdCard(String idCard) {
|
|
|
- if (idCard == null || idCard.isEmpty()) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- try {
|
|
|
- if (idCard.length() == 15) {
|
|
|
- // 15位身份证:出生日期在第6-12位
|
|
|
- String year = "19" + idCard.substring(6, 8);
|
|
|
- String month = idCard.substring(8, 10);
|
|
|
- String day = idCard.substring(10, 12);
|
|
|
- return String.format("%s-%s-%s", year, month, day);
|
|
|
- } else if (idCard.length() == 18) {
|
|
|
- // 18位身份证:出生日期在第6-14位
|
|
|
- String year = idCard.substring(6, 10);
|
|
|
- String month = idCard.substring(10, 12);
|
|
|
- String day = idCard.substring(12, 14);
|
|
|
- return String.format("%s-%s-%s", year, month, day);
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- // 忽略解析异常
|
|
|
- }
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 通过邀请码加入家庭
|
|
|
- */
|
|
|
- public boolean joinFamily(Long userId, String inviteCode) {
|
|
|
- if (inviteCode == null || inviteCode.isEmpty()) {
|
|
|
- throw new RuntimeException("邀请码不能为空");
|
|
|
- }
|
|
|
- // 查找家庭
|
|
|
- Family family = familyMapper.selectOne(new LambdaQueryWrapper<Family>()
|
|
|
- .eq(Family::getInviteCode, inviteCode));
|
|
|
- if (family == null) {
|
|
|
- throw new RuntimeException("邀请码无效");
|
|
|
- }
|
|
|
- // 更新用户的家庭ID
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- throw new RuntimeException("用户不存在");
|
|
|
- }
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
-
|
|
|
- // 如果家庭绑定了成长规划师,自动将家庭ID添加到成长规划师的 teacherFamilyIds
|
|
|
- if (family.getTeacherId() != null) {
|
|
|
- User teacher = userMapper.selectById(family.getTeacherId());
|
|
|
- if (teacher != null && "teacher".equals(teacher.getRole())) {
|
|
|
- String currentIds = teacher.getTeacherFamilyIds();
|
|
|
- String newFamilyId = String.valueOf(family.getId());
|
|
|
- if (currentIds == null || !currentIds.contains(newFamilyId)) {
|
|
|
- String updatedIds = currentIds == null ? newFamilyId : currentIds + "," + newFamilyId;
|
|
|
- teacher.setTeacherFamilyIds(updatedIds);
|
|
|
- teacher.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(teacher);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 为用户创建新家庭
|
|
|
- */
|
|
|
- public Long createFamilyForUser(Long userId, String familyName) {
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- throw new RuntimeException("用户不存在");
|
|
|
- }
|
|
|
- // 创建新家庭
|
|
|
- Family family = new Family();
|
|
|
- family.setName(familyName);
|
|
|
- family.setInviteCode(generateInviteCode());
|
|
|
- family.setCreatedAt(new Date());
|
|
|
- family.setUpdatedAt(new Date());
|
|
|
- familyMapper.insert(family);
|
|
|
- // 更新用户的家庭ID
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- return family.getId();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 使用邀请码注册并加入家庭
|
|
|
- */
|
|
|
- public LoginResultDTO registerWithInviteCode(RegisterWithInviteCodeDTO dto) {
|
|
|
- String phone = dto.getPhone();
|
|
|
- String inviteCode = dto.getInviteCode();
|
|
|
- String role = dto.getRole() != null ? dto.getRole() : "parent";
|
|
|
- String nickname = dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7);
|
|
|
-
|
|
|
- // 查找家庭
|
|
|
- Family family = familyMapper.selectOne(new LambdaQueryWrapper<Family>()
|
|
|
- .eq(Family::getInviteCode, inviteCode));
|
|
|
-
|
|
|
- User user;
|
|
|
- boolean isNewUser = false;
|
|
|
-
|
|
|
- if (family == null) {
|
|
|
- throw new RuntimeException("邀请码无效");
|
|
|
- }
|
|
|
-
|
|
|
- // 检查手机号是否已注册
|
|
|
- User existingUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
- .eq(User::getPhone, phone));
|
|
|
-
|
|
|
- if (existingUser != null) {
|
|
|
- // 手机号已注册,加入家庭
|
|
|
- user = existingUser;
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(user);
|
|
|
- } else {
|
|
|
- // 创建新用户并加入家庭
|
|
|
- isNewUser = true;
|
|
|
- user = new User();
|
|
|
- user.setOpenid("phone:" + phone);
|
|
|
- user.setUnionid("");
|
|
|
- user.setFamilyId(family.getId());
|
|
|
- user.setRole(role);
|
|
|
- user.setNickname(nickname);
|
|
|
- user.setPhone(phone);
|
|
|
- user.setPassword("");
|
|
|
- user.setCreatedAt(new Date());
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- userMapper.insert(user);
|
|
|
- }
|
|
|
-
|
|
|
- // 如果家庭绑定了成长规划师,自动将家庭ID添加到成长规划师的 teacherFamilyIds
|
|
|
- if (family.getTeacherId() != null) {
|
|
|
- User teacher = userMapper.selectById(family.getTeacherId());
|
|
|
- if (teacher != null && "teacher".equals(teacher.getRole())) {
|
|
|
- String currentIds = teacher.getTeacherFamilyIds();
|
|
|
- String newFamilyId = String.valueOf(family.getId());
|
|
|
- if (currentIds == null || !currentIds.contains(newFamilyId)) {
|
|
|
- String updatedIds = currentIds == null ? newFamilyId : currentIds + "," + newFamilyId;
|
|
|
- teacher.setTeacherFamilyIds(updatedIds);
|
|
|
- teacher.setUpdatedAt(new Date());
|
|
|
- userMapper.updateById(teacher);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 生成token
|
|
|
- String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
-
|
|
|
- LoginResultDTO result = new LoginResultDTO();
|
|
|
- result.setToken(token);
|
|
|
- result.setUserId(user.getId());
|
|
|
- result.setRole(user.getRole());
|
|
|
- result.setRoles(user.getRoles());
|
|
|
- result.setFamilyId(user.getFamilyId());
|
|
|
- result.setNickname(user.getNickname());
|
|
|
- result.setIsNewUser(isNewUser);
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 重置用户密码(管理员功能)
|
|
|
- * @param userId 用户ID
|
|
|
- * @param newPassword 新密码
|
|
|
- * @return 是否成功
|
|
|
- */
|
|
|
- public boolean resetPassword(Long userId, String newPassword) {
|
|
|
- if (userId == null || newPassword == null || newPassword.length() < 6) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- User user = userMapper.selectById(userId);
|
|
|
- if (user == null) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- String encryptedPassword = DigestUtils.md5DigestAsHex(("xzyj_user_" + newPassword).getBytes(StandardCharsets.UTF_8));
|
|
|
- user.setPassword(encryptedPassword);
|
|
|
- user.setUpdatedAt(new Date());
|
|
|
- return userMapper.updateById(user) > 0;
|
|
|
- }
|
|
|
-}
|
|
|
+package com.zxyj.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.zxyj.config.JwtConfig;
|
|
|
+import com.zxyj.dto.*;
|
|
|
+import com.zxyj.dto.RegisterWithIdCardDTO;
|
|
|
+import com.zxyj.dto.RegisterWithInviteCodeDTO;
|
|
|
+import com.zxyj.entity.Child;
|
|
|
+import com.zxyj.entity.Family;
|
|
|
+import com.zxyj.entity.User;
|
|
|
+import com.zxyj.mapper.ChildMapper;
|
|
|
+import com.zxyj.mapper.FamilyMapper;
|
|
|
+import com.zxyj.mapper.UserMapper;
|
|
|
+import com.zxyj.service.VerificationCodeService;
|
|
|
+import com.zxyj.service.WechatService;
|
|
|
+import com.zxyj.service.api.UserServiceInterface;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.util.DigestUtils;
|
|
|
+
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+
|
|
|
+@Service
|
|
|
+public class UserService implements UserServiceInterface {
|
|
|
+
|
|
|
+@Resource
|
|
|
+private UserMapper userMapper;
|
|
|
+
|
|
|
+@Resource
|
|
|
+private FamilyMapper familyMapper;
|
|
|
+
|
|
|
+@Resource
|
|
|
+private ChildMapper childMapper;
|
|
|
+
|
|
|
+@Resource
|
|
|
+private JwtConfig jwtConfig;
|
|
|
+
|
|
|
+@Resource
|
|
|
+private VerificationCodeService verificationCodeService;
|
|
|
+
|
|
|
+@Resource
|
|
|
+private WechatService wechatService;
|
|
|
+
|
|
|
+ public LoginResultDTO wechatLogin(WechatLoginDTO dto) {
|
|
|
+ // 模拟微信登录,实际需要调用微信API获取openid
|
|
|
+ String openid = "mock_openid_" + dto.getCode();
|
|
|
+
|
|
|
+ // 查询用户是否存在
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getOpenid, openid));
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ // 新用户,创建家庭和用户
|
|
|
+ Family family = new Family();
|
|
|
+ family.setName("我的家庭");
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+
|
|
|
+ user = new User();
|
|
|
+ user.setOpenid(openid);
|
|
|
+ user.setUnionid("");
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setRole("parent");
|
|
|
+ user.setNickname(dto.getNickname());
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ user.setPassword("");
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机号验证码登录
|
|
|
+ */
|
|
|
+ public LoginResultDTO phoneLogin(PhoneLoginDTO dto) {
|
|
|
+ // 使用手机号作为唯一标识
|
|
|
+ String phone = dto.getPhone();
|
|
|
+
|
|
|
+ // 先通过手机号查找用户(更准确的判断)
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getPhone, phone));
|
|
|
+
|
|
|
+ boolean isNewUser = false;
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ // 新用户,创建新家庭和用户
|
|
|
+ isNewUser = true;
|
|
|
+
|
|
|
+ Family family = new Family();
|
|
|
+ family.setName("我的家庭");
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+
|
|
|
+ user = new User();
|
|
|
+ user.setOpenid("phone:" + phone);
|
|
|
+ user.setUnionid("");
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ // 默认角色为家长,可以被覆盖
|
|
|
+ user.setRole(dto.getRole() != null ? dto.getRole() : "parent");
|
|
|
+ user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7));
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ user.setPhone(phone);
|
|
|
+ user.setBirthday(dto.getBirthday());
|
|
|
+ user.setGender(dto.getGender());
|
|
|
+ user.setPassword("");
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+ } else {
|
|
|
+ // 老用户,更新信息(如果提供了)
|
|
|
+ if (dto.getNickname() != null && !dto.getNickname().isEmpty()) {
|
|
|
+ user.setNickname(dto.getNickname());
|
|
|
+ }
|
|
|
+ if (dto.getAvatar() != null && !dto.getAvatar().isEmpty()) {
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ }
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(isNewUser);
|
|
|
+ result.setNeedsRoleSelect(isNewUser);
|
|
|
+
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信手机号登录(正确获取openid和手机号)
|
|
|
+ */
|
|
|
+ public LoginResultDTO wechatPhoneLogin(WechatPhoneLoginDTO dto) {
|
|
|
+ // 1. 通过code获取openid
|
|
|
+ Map<String, String> sessionData = wechatService.code2Session(dto.getCode());
|
|
|
+ String openid = sessionData.get("openid");
|
|
|
+ String unionid = sessionData.get("unionid");
|
|
|
+
|
|
|
+ // 2. 通过手机号code获取真实手机号
|
|
|
+ String phone = wechatService.getPhoneNumber(dto.getPhoneCode());
|
|
|
+
|
|
|
+ // 3. 先通过openid查找用户
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getOpenid, openid));
|
|
|
+
|
|
|
+ boolean isNewUser = false;
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ // 4. openid不存在,通过手机号查找
|
|
|
+ user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getPhone, phone));
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ // 5. 全新用户,创建新家庭和用户
|
|
|
+ isNewUser = true;
|
|
|
+
|
|
|
+ Family family = new Family();
|
|
|
+ family.setName("我的家庭");
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+
|
|
|
+ user = new User();
|
|
|
+ user.setOpenid(openid);
|
|
|
+ user.setUnionid(unionid);
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setRole("parent");
|
|
|
+ user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7));
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ user.setPhone(phone);
|
|
|
+ user.setPassword("");
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+ } else {
|
|
|
+ // 6. 手机号已存在但openid不同,绑定openid
|
|
|
+ user.setOpenid(openid);
|
|
|
+ user.setUnionid(unionid);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 7. openid已存在,更新手机号(如果手机号变了)
|
|
|
+ if (phone != null && !phone.equals(user.getPhone())) {
|
|
|
+ user.setPhone(phone);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(isNewUser);
|
|
|
+ result.setOpenid(openid);
|
|
|
+
|
|
|
+ // 新用户需要完善信息(仅当需要选择角色时)
|
|
|
+ result.setNeedsRoleSelect(isNewUser);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信openid静默登录(用户再次打开小程序时自动登录)
|
|
|
+ */
|
|
|
+ public LoginResultDTO silentLogin(String code) {
|
|
|
+ // 1. 通过code获取openid
|
|
|
+ Map<String, String> sessionData = wechatService.code2Session(code);
|
|
|
+ String openid = sessionData.get("openid");
|
|
|
+ String unionid = sessionData.get("unionid");
|
|
|
+
|
|
|
+ // 2. 查找用户
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getOpenid, openid));
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ throw new RuntimeException("用户不存在,请先登录");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(false);
|
|
|
+ result.setOpenid(openid);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过openid自动登录(前端已缓存openid时调用)
|
|
|
+ */
|
|
|
+ public LoginResultDTO autoLoginByOpenid(String openid) {
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getOpenid, openid));
|
|
|
+
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(false);
|
|
|
+ result.setOpenid(openid);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean setPassword(Long userId, String password) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ user.setPassword(encryptPassword(password));
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean verifyPassword(Long userId, String password) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null || user.getPassword() == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return user.getPassword().equals(encryptPassword(password));
|
|
|
+ }
|
|
|
+
|
|
|
+ public User getUserInfo(Long userId) {
|
|
|
+ return userMapper.selectById(userId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查手机号是否已注册
|
|
|
+ * @param phone 手机号
|
|
|
+ * @return true=已注册,false=未注册
|
|
|
+ */
|
|
|
+ public boolean isPhoneRegistered(String phone) {
|
|
|
+ if (phone == null || phone.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getPhone, phone));
|
|
|
+ return user != null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 直接注册(测试环境:不验证验证码)
|
|
|
+ */
|
|
|
+ public LoginResultDTO directRegister(DirectRegisterDTO dto) {
|
|
|
+ // 创建家庭
|
|
|
+ Family family = new Family();
|
|
|
+ family.setName("我的家庭");
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+
|
|
|
+ // 创建用户
|
|
|
+ User user = new User();
|
|
|
+ user.setOpenid("phone:" + dto.getPhone());
|
|
|
+ user.setUnionid("");
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setRole(dto.getRole() != null ? dto.getRole() : "parent");
|
|
|
+ user.setNickname(dto.getNickname() != null ? dto.getNickname() : "用户" + dto.getPhone().substring(7));
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ user.setPhone(dto.getPhone());
|
|
|
+ user.setBirthday(dto.getBirthday());
|
|
|
+ user.setGender(dto.getGender());
|
|
|
+ user.setPassword("");
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+
|
|
|
+ // 如果有邀请码,加入家庭
|
|
|
+ if (dto.getInviteCode() != null && !dto.getInviteCode().isEmpty()) {
|
|
|
+ try {
|
|
|
+ joinFamily(user.getId(), dto.getInviteCode());
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略邀请码错误,继续使用创建的家庭
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(true);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public String switchMode(Long userId, String mode) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 验证用户是否拥有该角色
|
|
|
+ if (!hasRole(user, mode)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ user.setRole(mode);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return mode;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 切换用户当前角色 (支持 parent/child/teacher)
|
|
|
+ * 用户需要先拥有该角色才能切换
|
|
|
+ */
|
|
|
+ public String switchRole(Long userId, String targetRole) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (!hasRole(user, targetRole)) {
|
|
|
+ throw new RuntimeException("您没有该角色权限");
|
|
|
+ }
|
|
|
+ if (!isValidRole(targetRole)) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ user.setRole(targetRole);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return targetRole;
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoginResultDTO switchRoleForLogin(Long userId, String role) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ throw new RuntimeException("用户不存在");
|
|
|
+ }
|
|
|
+ if ("teacher".equals(role) && !hasRole(user, "teacher")) {
|
|
|
+ throw new RuntimeException("您不是成长规划师,无法切换为规划师模式");
|
|
|
+ }
|
|
|
+ user.setRole(role);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(false);
|
|
|
+ populateTeacherFields(result, user);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户所有角色列表
|
|
|
+ */
|
|
|
+ public List<String> getUserRoles(Long userId) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ return parseRoles(user.getRoles());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查用户是否拥有指定角色
|
|
|
+ */
|
|
|
+ public boolean hasRole(Long userId, String role) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return hasRole(user, role);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加角色到用户 (用于用户注册成为老师时)
|
|
|
+ */
|
|
|
+ public boolean addRole(Long userId, String newRole) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null || !isValidRole(newRole)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 如果用户还没有roles字段,设置默认值
|
|
|
+ if (user.getRoles() == null || user.getRoles().isEmpty()) {
|
|
|
+ user.setRoles(user.getRole()); // 当前角色作为第一个角色
|
|
|
+ }
|
|
|
+ // 检查是否已拥有该角色
|
|
|
+ if (hasRole(user, newRole)) {
|
|
|
+ return true; // 已有该角色
|
|
|
+ }
|
|
|
+ // 添加新角色
|
|
|
+ List<String> roleList = parseRoles(user.getRoles());
|
|
|
+ roleList.add(newRole);
|
|
|
+ user.setRoles(joinRoles(roleList));
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查用户是否拥有指定角色 (内部方法)
|
|
|
+ */
|
|
|
+ private boolean hasRole(User user, String role) {
|
|
|
+ List<String> roles = parseRoles(user.getRoles());
|
|
|
+ return roles.contains(role);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析角色字符串为列表
|
|
|
+ */
|
|
|
+ private List<String> parseRoles(String rolesStr) {
|
|
|
+ List<String> result = new ArrayList<>();
|
|
|
+ if (rolesStr == null || rolesStr.isEmpty()) {
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ // 支持JSON数组格式或逗号分隔格式
|
|
|
+ if (rolesStr.startsWith("[")) {
|
|
|
+ // JSON格式解析
|
|
|
+ rolesStr = rolesStr.replace("[", "").replace("]", "")
|
|
|
+ .replace("\"", "").replace(" ", "");
|
|
|
+ String[] arr = rolesStr.split(",");
|
|
|
+ for (String r : arr) {
|
|
|
+ if (!r.isEmpty()) {
|
|
|
+ result.add(r.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 逗号分隔格式
|
|
|
+ String[] arr = rolesStr.split(",");
|
|
|
+ for (String r : arr) {
|
|
|
+ if (!r.trim().isEmpty()) {
|
|
|
+ result.add(r.trim());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将角色列表转换为字符串
|
|
|
+ */
|
|
|
+ private String joinRoles(List<String> roles) {
|
|
|
+ return String.join(",", roles);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查角色是否有效
|
|
|
+ */
|
|
|
+ private boolean isValidRole(String role) {
|
|
|
+ return "parent".equals(role) || "child".equals(role) || "teacher".equals(role);
|
|
|
+ }
|
|
|
+
|
|
|
+/**
|
|
|
+ * 家长切换到指定孩子身份(视图切换,不修改用户的实际角色)
|
|
|
+ * 家长可以在家长模式下查看孩子的任务和积分
|
|
|
+ */
|
|
|
+ public Long switchToChild(Long userId, Long childId) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ Child child = childMapper.selectById(childId);
|
|
|
+ if (child == null || !child.getFamilyId().equals(user.getFamilyId())) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 注意:不修改用户的 role 字段,只是返回 childId 让前端切换视图
|
|
|
+ // 前端应该使用 currentRole 或 viewMode 来控制显示模式
|
|
|
+ return childId;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从孩子视图切换回家长视图
|
|
|
+ * 这只是确认切换操作,不需要修改数据库
|
|
|
+ */
|
|
|
+ public boolean switchBackToParent(Long userId) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 不需要修改用户的 role 字段
|
|
|
+ // 只是确认操作成功,前端切换视图
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ public LoginResultDTO registerWithIdCard(RegisterWithIdCardDTO dto) {
|
|
|
+ // 验证验证码
|
|
|
+ boolean valid = verificationCodeService.verifyCode(dto.getPhone(), dto.getCode(), "login");
|
|
|
+ if (!valid) {
|
|
|
+ throw new RuntimeException("验证码错误或已过期");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通过身份证查找是否已有注册的用户
|
|
|
+ User existingUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getIdCard, dto.getIdCard()));
|
|
|
+
|
|
|
+ Family family;
|
|
|
+ User user;
|
|
|
+
|
|
|
+ if (existingUser != null) {
|
|
|
+ // 如果存在相同身份证的用户,加入其家庭
|
|
|
+ user = existingUser;
|
|
|
+ family = familyMapper.selectById(user.getFamilyId());
|
|
|
+
|
|
|
+ // 更新用户信息(昵称和头像可以更新)
|
|
|
+ user.setNickname(dto.getNickname());
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ // 手机号也可以更新(如果需要)
|
|
|
+ user.setOpenid("phone:" + dto.getPhone());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ } else {
|
|
|
+ // 创建新家庭和用户
|
|
|
+ family = new Family();
|
|
|
+ family.setName("我的家庭");
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+
|
|
|
+ user = new User();
|
|
|
+ user.setOpenid("phone:" + dto.getPhone());
|
|
|
+ user.setUnionid("");
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setRole("parent");
|
|
|
+ user.setNickname(dto.getNickname());
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ user.setIdCard(dto.getIdCard());
|
|
|
+ user.setRealName(dto.getRealName());
|
|
|
+ user.setPassword(""); // 初始密码为空,需要后续设置
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public ChildInfoDTO createChild(Long userId, CreateChildDTO dto) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dto.getNickname() == null || dto.getNickname().trim().isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("孩子昵称不能为空");
|
|
|
+ }
|
|
|
+ if (dto.getAge() == null || dto.getAge() < 1 || dto.getAge() > 18) {
|
|
|
+ throw new IllegalArgumentException("请输入有效的年龄(1-18岁)");
|
|
|
+ }
|
|
|
+
|
|
|
+ Child child = new Child();
|
|
|
+ child.setUserId(userId);
|
|
|
+ child.setFamilyId(user.getFamilyId());
|
|
|
+ child.setNickname(dto.getNickname().trim());
|
|
|
+ child.setAge(dto.getAge());
|
|
|
+ child.setBirthday(dto.getBirthday());
|
|
|
+ child.setGender(dto.getGender());
|
|
|
+ child.setDanLevel(dto.getDanLevel());
|
|
|
+ child.setPhone(dto.getPhone());
|
|
|
+ child.setIdCard(dto.getIdCard());
|
|
|
+ // 扣分开关:优先使用传入值,否则根据年龄判断
|
|
|
+ if (dto.getPenaltyEnabled() != null) {
|
|
|
+ child.setPenaltyEnabled(dto.getPenaltyEnabled());
|
|
|
+ } else {
|
|
|
+ // 年龄可能为空,默认不扣分
|
|
|
+ Integer age = dto.getAge();
|
|
|
+ child.setPenaltyEnabled(age != null && age >= 6 ? 1 : 0);
|
|
|
+ }
|
|
|
+ child.setTheme("default");
|
|
|
+ child.setTotalPoints(0);
|
|
|
+ child.setStreakDays(0);
|
|
|
+ child.setFocusMaxDaily(3);
|
|
|
+ child.setFocusRemaining(3);
|
|
|
+ child.setFocusResetDate(new Date());
|
|
|
+ child.setCreatedAt(new Date());
|
|
|
+ child.setUpdatedAt(new Date());
|
|
|
+ childMapper.insert(child);
|
|
|
+
|
|
|
+ return toChildInfoDTO(child);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<ChildInfoDTO> getChildren(Long userId) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ List<Child> children = childMapper.selectList(new LambdaQueryWrapper<Child>()
|
|
|
+ .eq(Child::getFamilyId, user.getFamilyId()));
|
|
|
+
|
|
|
+ return children.stream().map(this::toChildInfoDTO).collect(java.util.stream.Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean updateChild(Long userId, Long childId, UpdateChildDTO dto) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ Child child = childMapper.selectById(childId);
|
|
|
+ if (child == null || child.getFamilyId() == null || !child.getFamilyId().equals(user.getFamilyId())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dto.getNickname() != null) {
|
|
|
+ child.setNickname(dto.getNickname());
|
|
|
+ }
|
|
|
+ if (dto.getAge() != null) {
|
|
|
+ child.setAge(dto.getAge());
|
|
|
+ }
|
|
|
+ if (dto.getBirthday() != null) {
|
|
|
+ child.setBirthday(dto.getBirthday());
|
|
|
+ }
|
|
|
+ if (dto.getGender() != null) {
|
|
|
+ child.setGender(dto.getGender());
|
|
|
+ }
|
|
|
+ if (dto.getDanLevel() != null) {
|
|
|
+ child.setDanLevel(dto.getDanLevel());
|
|
|
+ }
|
|
|
+ if (dto.getPhone() != null) {
|
|
|
+ child.setPhone(dto.getPhone());
|
|
|
+ }
|
|
|
+ if (dto.getIdCard() != null) {
|
|
|
+ child.setIdCard(dto.getIdCard());
|
|
|
+ }
|
|
|
+ if (dto.getPenaltyEnabled() != null) {
|
|
|
+ child.setPenaltyEnabled(dto.getPenaltyEnabled());
|
|
|
+ }
|
|
|
+ if (dto.getTheme() != null) {
|
|
|
+ child.setTheme(dto.getTheme());
|
|
|
+ }
|
|
|
+ child.setUpdatedAt(new Date());
|
|
|
+ childMapper.updateById(child);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private String encryptPassword(String password) {
|
|
|
+ return DigestUtils.md5DigestAsHex(("xzyj_salt_" + password).getBytes(StandardCharsets.UTF_8));
|
|
|
+ }
|
|
|
+
|
|
|
+ private String generateInviteCode() {
|
|
|
+ return UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
|
|
+ }
|
|
|
+
|
|
|
+ private ChildInfoDTO toChildInfoDTO(Child child) {
|
|
|
+ ChildInfoDTO dto = new ChildInfoDTO();
|
|
|
+ dto.setId(child.getId());
|
|
|
+ dto.setNickname(child.getNickname());
|
|
|
+ dto.setAge(child.getAge());
|
|
|
+ dto.setBirthday(child.getBirthday());
|
|
|
+ dto.setGender(child.getGender());
|
|
|
+ dto.setDanLevel(child.getDanLevel());
|
|
|
+ dto.setPhone(child.getPhone());
|
|
|
+ dto.setIdCard(child.getIdCard());
|
|
|
+ dto.setPenaltyEnabled(child.getPenaltyEnabled());
|
|
|
+ dto.setTheme(child.getTheme());
|
|
|
+ dto.setTotalPoints(child.getTotalPoints());
|
|
|
+ dto.setStreakDays(child.getStreakDays());
|
|
|
+ dto.setFocusRemaining(child.getFocusRemaining());
|
|
|
+ return dto;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新用户信息
|
|
|
+ */
|
|
|
+ public boolean updateUserInfo(Long userId, UpdateUserDTO dto) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (dto.getNickname() != null) {
|
|
|
+ user.setNickname(dto.getNickname());
|
|
|
+ }
|
|
|
+ if (dto.getAvatar() != null) {
|
|
|
+ user.setAvatar(dto.getAvatar());
|
|
|
+ }
|
|
|
+ if (dto.getRealName() != null) {
|
|
|
+ user.setRealName(dto.getRealName());
|
|
|
+ }
|
|
|
+ // 更新角色
|
|
|
+ if (dto.getRole() != null) {
|
|
|
+ user.setRole(dto.getRole());
|
|
|
+ }
|
|
|
+ // 更新性别
|
|
|
+ if (dto.getGender() != null) {
|
|
|
+ user.setGender(dto.getGender());
|
|
|
+ }
|
|
|
+ // 更新生日(将字符串转换为Date)
|
|
|
+ if (dto.getBirthday() != null && !dto.getBirthday().isEmpty()) {
|
|
|
+ try {
|
|
|
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ user.setBirthday(sdf.parse(dto.getBirthday()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略解析错误
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 更新身份证号
|
|
|
+ if (dto.getIdCard() != null && !dto.getIdCard().isEmpty()) {
|
|
|
+ user.setIdCard(dto.getIdCard());
|
|
|
+ // 如果没有手动设置生日,尝试从身份证号提取
|
|
|
+ if (user.getBirthday() == null) {
|
|
|
+ String birthdayFromIdCard = extractBirthdayFromIdCard(dto.getIdCard());
|
|
|
+ if (birthdayFromIdCard != null) {
|
|
|
+ try {
|
|
|
+ java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ user.setBirthday(sdf.parse(birthdayFromIdCard));
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略解析错误
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 更新成长规划师信息
|
|
|
+ if (dto.getTeacherNo() != null) {
|
|
|
+ user.setTeacherNo(dto.getTeacherNo());
|
|
|
+ }
|
|
|
+ if (dto.getTeacherPhoto() != null) {
|
|
|
+ user.setTeacherPhoto(dto.getTeacherPhoto());
|
|
|
+ }
|
|
|
+ if (dto.getCertificateImage() != null) {
|
|
|
+ user.setCertificateImage(dto.getCertificateImage());
|
|
|
+ }
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从身份证号提取生日
|
|
|
+ * 15位:1985年07月08日 -> 1985-07-08
|
|
|
+ * 18位:1985年07月08日 -> 1985-07-08
|
|
|
+ */
|
|
|
+ private String extractBirthdayFromIdCard(String idCard) {
|
|
|
+ if (idCard == null || idCard.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ if (idCard.length() == 15) {
|
|
|
+ // 15位身份证:出生日期在第6-12位
|
|
|
+ String year = "19" + idCard.substring(6, 8);
|
|
|
+ String month = idCard.substring(8, 10);
|
|
|
+ String day = idCard.substring(10, 12);
|
|
|
+ return String.format("%s-%s-%s", year, month, day);
|
|
|
+ } else if (idCard.length() == 18) {
|
|
|
+ // 18位身份证:出生日期在第6-14位
|
|
|
+ String year = idCard.substring(6, 10);
|
|
|
+ String month = idCard.substring(10, 12);
|
|
|
+ String day = idCard.substring(12, 14);
|
|
|
+ return String.format("%s-%s-%s", year, month, day);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 忽略解析异常
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 通过邀请码加入家庭
|
|
|
+ */
|
|
|
+ public boolean joinFamily(Long userId, String inviteCode) {
|
|
|
+ if (inviteCode == null || inviteCode.isEmpty()) {
|
|
|
+ throw new RuntimeException("邀请码不能为空");
|
|
|
+ }
|
|
|
+ // 查找家庭
|
|
|
+ Family family = familyMapper.selectOne(new LambdaQueryWrapper<Family>()
|
|
|
+ .eq(Family::getInviteCode, inviteCode));
|
|
|
+ if (family == null) {
|
|
|
+ throw new RuntimeException("邀请码无效");
|
|
|
+ }
|
|
|
+ // 更新用户的家庭ID
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ throw new RuntimeException("用户不存在");
|
|
|
+ }
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+
|
|
|
+ // 如果家庭绑定了成长规划师,自动将家庭ID添加到成长规划师的 teacherFamilyIds
|
|
|
+ if (family.getTeacherId() != null) {
|
|
|
+ User teacher = userMapper.selectById(family.getTeacherId());
|
|
|
+ if (teacher != null && "teacher".equals(teacher.getRole())) {
|
|
|
+ String currentIds = teacher.getTeacherFamilyIds();
|
|
|
+ String newFamilyId = String.valueOf(family.getId());
|
|
|
+ if (currentIds == null || !currentIds.contains(newFamilyId)) {
|
|
|
+ String updatedIds = currentIds == null ? newFamilyId : currentIds + "," + newFamilyId;
|
|
|
+ teacher.setTeacherFamilyIds(updatedIds);
|
|
|
+ teacher.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(teacher);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 为用户创建新家庭
|
|
|
+ */
|
|
|
+ public Long createFamilyForUser(Long userId, String familyName) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ throw new RuntimeException("用户不存在");
|
|
|
+ }
|
|
|
+ // 创建新家庭
|
|
|
+ Family family = new Family();
|
|
|
+ family.setName(familyName);
|
|
|
+ family.setInviteCode(generateInviteCode());
|
|
|
+ family.setCreatedAt(new Date());
|
|
|
+ family.setUpdatedAt(new Date());
|
|
|
+ familyMapper.insert(family);
|
|
|
+ // 更新用户的家庭ID
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return family.getId();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用邀请码注册并加入家庭
|
|
|
+ */
|
|
|
+ public LoginResultDTO registerWithInviteCode(RegisterWithInviteCodeDTO dto) {
|
|
|
+ String phone = dto.getPhone();
|
|
|
+ String inviteCode = dto.getInviteCode();
|
|
|
+ String role = dto.getRole() != null ? dto.getRole() : "parent";
|
|
|
+ String nickname = dto.getNickname() != null ? dto.getNickname() : "用户" + phone.substring(7);
|
|
|
+
|
|
|
+ // 查找家庭
|
|
|
+ Family family = familyMapper.selectOne(new LambdaQueryWrapper<Family>()
|
|
|
+ .eq(Family::getInviteCode, inviteCode));
|
|
|
+
|
|
|
+ User user;
|
|
|
+ boolean isNewUser = false;
|
|
|
+
|
|
|
+ if (family == null) {
|
|
|
+ throw new RuntimeException("邀请码无效");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查手机号是否已注册
|
|
|
+ User existingUser = userMapper.selectOne(new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getPhone, phone));
|
|
|
+
|
|
|
+ if (existingUser != null) {
|
|
|
+ // 手机号已注册,加入家庭
|
|
|
+ user = existingUser;
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ } else {
|
|
|
+ // 创建新用户并加入家庭
|
|
|
+ isNewUser = true;
|
|
|
+ user = new User();
|
|
|
+ user.setOpenid("phone:" + phone);
|
|
|
+ user.setUnionid("");
|
|
|
+ user.setFamilyId(family.getId());
|
|
|
+ user.setRole(role);
|
|
|
+ user.setNickname(nickname);
|
|
|
+ user.setPhone(phone);
|
|
|
+ user.setPassword("");
|
|
|
+ user.setCreatedAt(new Date());
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.insert(user);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果家庭绑定了成长规划师,自动将家庭ID添加到成长规划师的 teacherFamilyIds
|
|
|
+ if (family.getTeacherId() != null) {
|
|
|
+ User teacher = userMapper.selectById(family.getTeacherId());
|
|
|
+ if (teacher != null && "teacher".equals(teacher.getRole())) {
|
|
|
+ String currentIds = teacher.getTeacherFamilyIds();
|
|
|
+ String newFamilyId = String.valueOf(family.getId());
|
|
|
+ if (currentIds == null || !currentIds.contains(newFamilyId)) {
|
|
|
+ String updatedIds = currentIds == null ? newFamilyId : currentIds + "," + newFamilyId;
|
|
|
+ teacher.setTeacherFamilyIds(updatedIds);
|
|
|
+ teacher.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(teacher);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成token
|
|
|
+ String token = jwtConfig.generateToken(user.getId(), user.getRole());
|
|
|
+
|
|
|
+ LoginResultDTO result = new LoginResultDTO();
|
|
|
+ result.setToken(token);
|
|
|
+ result.setUserId(user.getId());
|
|
|
+ result.setRole(user.getRole());
|
|
|
+ result.setRoles(user.getRoles());
|
|
|
+ result.setFamilyId(user.getFamilyId());
|
|
|
+ result.setNickname(user.getNickname());
|
|
|
+ result.setIsNewUser(isNewUser);
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 重置用户密码(管理员功能)
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param newPassword 新密码
|
|
|
+ * @return 是否成功
|
|
|
+ */
|
|
|
+ public boolean resetPassword(Long userId, String newPassword) {
|
|
|
+ if (userId == null || newPassword == null || newPassword.length() < 6) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ String encryptedPassword = DigestUtils.md5DigestAsHex(("xzyj_user_" + newPassword).getBytes(StandardCharsets.UTF_8));
|
|
|
+ user.setPassword(encryptedPassword);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ return userMapper.updateById(user) > 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void populateTeacherFields(LoginResultDTO result, User user) {
|
|
|
+ result.setTeacherStatus(user.getTeacherStatus() != null ? user.getTeacherStatus() : "pending");
|
|
|
+ boolean hasTeacher = hasRole(user, "teacher");
|
|
|
+ result.setHasTeacherRole(hasTeacher);
|
|
|
+ }
|
|
|
+}
|