X723595506 2 долоо хоног өмнө
parent
commit
91a2167b58
52 өөрчлөгдсөн 1143 нэмэгдсэн , 0 устгасан
  1. 52 0
      src/main/java/com/futu/goose/common/exception/RRException.java
  2. 32 0
      src/main/java/com/futu/goose/common/util/Constant.java
  3. 32 0
      src/main/java/com/futu/goose/common/util/Md5Util.java
  4. 62 0
      src/main/java/com/futu/goose/common/util/Result.java
  5. 112 0
      src/main/java/com/futu/goose/common/util/TokenUtils.java
  6. 34 0
      src/main/java/com/futu/goose/common/util/WxService.java
  7. 16 0
      src/main/java/com/futu/goose/user/config/RedisConfig.java
  8. 6 0
      src/main/java/com/futu/goose/user/config/RedisKey.java
  9. 38 0
      src/main/java/com/futu/goose/user/config/RedissionConfig.java
  10. 4 0
      src/main/java/com/futu/goose/user/controller/UserController.java
  11. 29 0
      src/main/java/com/futu/goose/user/controller/WeChatController.java
  12. 20 0
      src/main/java/com/futu/goose/user/dao/TUserMapper.java
  13. 20 0
      src/main/java/com/futu/goose/user/dao/WechatUsersMapper.java
  14. 173 0
      src/main/java/com/futu/goose/user/domain/TUser.java
  15. 189 0
      src/main/java/com/futu/goose/user/domain/WechatUsers.java
  16. 15 0
      src/main/java/com/futu/goose/user/dto/WXAuth.java
  17. 13 0
      src/main/java/com/futu/goose/user/service/TUserService.java
  18. 18 0
      src/main/java/com/futu/goose/user/service/WechatUsersService.java
  19. 22 0
      src/main/java/com/futu/goose/user/service/impl/TUserServiceImpl.java
  20. 165 0
      src/main/java/com/futu/goose/user/service/impl/WechatUsersServiceImpl.java
  21. 24 0
      target/classes/application.yml
  22. BIN
      target/classes/com/futu/goose/BigGooseApplication.class
  23. BIN
      target/classes/com/futu/goose/common/exception/RRException.class
  24. BIN
      target/classes/com/futu/goose/common/util/Constant.class
  25. BIN
      target/classes/com/futu/goose/common/util/Md5Util.class
  26. BIN
      target/classes/com/futu/goose/common/util/Result.class
  27. BIN
      target/classes/com/futu/goose/common/util/TokenUtils.class
  28. BIN
      target/classes/com/futu/goose/common/util/WxService.class
  29. BIN
      target/classes/com/futu/goose/course/controller/CourseController.class
  30. BIN
      target/classes/com/futu/goose/discounts/controller/DiscountsController.class
  31. BIN
      target/classes/com/futu/goose/message/controller/MessageController.class
  32. BIN
      target/classes/com/futu/goose/order/controller/OrderController.class
  33. BIN
      target/classes/com/futu/goose/pay/controller/PayController.class
  34. BIN
      target/classes/com/futu/goose/store/controller/StoreController.class
  35. BIN
      target/classes/com/futu/goose/system/controller/SystemController.class
  36. BIN
      target/classes/com/futu/goose/teacher/controller/TeacherController.class
  37. BIN
      target/classes/com/futu/goose/user/config/RedisConfig.class
  38. BIN
      target/classes/com/futu/goose/user/config/RedisKey.class
  39. BIN
      target/classes/com/futu/goose/user/config/RedissionConfig.class
  40. BIN
      target/classes/com/futu/goose/user/controller/UserController.class
  41. BIN
      target/classes/com/futu/goose/user/controller/WeChatController.class
  42. BIN
      target/classes/com/futu/goose/user/dao/TUserMapper.class
  43. BIN
      target/classes/com/futu/goose/user/dao/WechatUsersMapper.class
  44. BIN
      target/classes/com/futu/goose/user/domain/TUser.class
  45. BIN
      target/classes/com/futu/goose/user/domain/WechatUsers.class
  46. BIN
      target/classes/com/futu/goose/user/dto/WXAuth.class
  47. BIN
      target/classes/com/futu/goose/user/service/TUserService.class
  48. BIN
      target/classes/com/futu/goose/user/service/WechatUsersService.class
  49. BIN
      target/classes/com/futu/goose/user/service/impl/TUserServiceImpl.class
  50. BIN
      target/classes/com/futu/goose/user/service/impl/WechatUsersServiceImpl.class
  51. 32 0
      target/classes/mapper/TUserMapper.xml
  52. 35 0
      target/classes/mapper/WechatUsersMapper.xml

+ 52 - 0
src/main/java/com/futu/goose/common/exception/RRException.java

@@ -0,0 +1,52 @@
+package com.futu.goose.common.exception;
+
+/**
+ * 自定义异常
+ *
+ */
+public class RRException extends RuntimeException {
+	private static final long serialVersionUID = 1L;
+
+    private String msg;
+    private int code = 500;
+
+    public RRException(String msg) {
+		super(msg);
+		this.msg = msg;
+	}
+
+	public RRException(String msg, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+	}
+
+	public RRException(String msg, int code) {
+		super(msg);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public RRException(String msg, int code, Throwable e) {
+		super(msg, e);
+		this.msg = msg;
+		this.code = code;
+	}
+
+	public String getMsg() {
+		return msg;
+	}
+
+	public void setMsg(String msg) {
+		this.msg = msg;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public void setCode(int code) {
+		this.code = code;
+	}
+
+
+}

+ 32 - 0
src/main/java/com/futu/goose/common/util/Constant.java

@@ -0,0 +1,32 @@
+package com.futu.goose.common.util;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @ClassName: Constant
+ * @Author:
+ * @Date: 2024年2月26日 14:05
+ */
+public class Constant {
+
+
+    public static String TOKEN_NAME = "Authorization";
+
+    /**
+     * 请求响应码常量
+     */
+    public static final Integer RESPONSE_CODE_SUCCESS = 200;
+    public static final Integer RESPONSE_CODE_ERROR = 400;
+    public static final Integer RESPONSE_CODE_NO_LOGIN = 402;
+    public static final Integer RESPONSE_CODE_FORBIDDEN = 403;
+
+    public static final Map<Integer, String> RESPONSE_CODE_MAP = new HashMap<>();
+    static {
+        RESPONSE_CODE_MAP.put(RESPONSE_CODE_SUCCESS, "请求成功");
+        RESPONSE_CODE_MAP.put(RESPONSE_CODE_ERROR, "请求失败");
+        RESPONSE_CODE_MAP.put(RESPONSE_CODE_NO_LOGIN, "没有登录");
+        RESPONSE_CODE_MAP.put(RESPONSE_CODE_FORBIDDEN, "权限不足");
+    }
+
+}

+ 32 - 0
src/main/java/com/futu/goose/common/util/Md5Util.java

@@ -0,0 +1,32 @@
+package com.futu.goose.common.util;
+
+import java.security.MessageDigest;
+
+public class Md5Util {
+    /**
+     * 获取String字符串的MD5
+     *
+     * @param s
+     * @return
+     */
+    public static String MD5(String s) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] bytes = md.digest(s.getBytes("utf-8"));
+            return toHex(bytes);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String toHex(byte[] bytes) {
+
+        final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+        StringBuilder ret = new StringBuilder(bytes.length * 2);
+        for (int i = 0; i < bytes.length; i++) {
+            ret.append(HEX_DIGITS[(bytes[i] >> 4) & 0x0f]);
+            ret.append(HEX_DIGITS[bytes[i] & 0x0f]);
+        }
+        return ret.toString();
+    }
+}

+ 62 - 0
src/main/java/com/futu/goose/common/util/Result.java

@@ -0,0 +1,62 @@
+package com.futu.goose.common.util;
+
+import lombok.Data;
+
+/**
+ * @ClassName: Result 返回结果
+ *  code 响应码 代表请求成功/失败
+ *  message 响应信息
+ *  data 响应数据
+ * @Author:
+ * @Date: 2024年2月26日 13:58
+ */
+@Data
+public class Result {
+    private Integer code;//状态码
+    private String message;//提示信息
+    private Object data;//数据
+
+    public Result(Integer code, String message, Object data) {
+        this.code = code;
+        this.message = message;
+        this.data = data;
+    }
+
+    public Result() {
+    }
+
+    //成功响应
+    public static Result OK() {
+        return new Result(Constant.RESPONSE_CODE_SUCCESS, "操作成功", null);
+    }
+
+    public static Result OK(Object data) {
+        return new Result(Constant.RESPONSE_CODE_SUCCESS, "操作成功", data);
+    }
+
+    //失败响应
+    public static Result ERROR() {
+        return new Result(Constant.RESPONSE_CODE_ERROR, "操作失败", null);
+    }
+
+    public static Result ERROR(Object data) {
+        return new Result(Constant.RESPONSE_CODE_ERROR, "操作失败", data);
+    }
+
+    public static Result ERROR(String message) {
+        return new Result(Constant.RESPONSE_CODE_ERROR, message, null);
+    }
+
+    //未登录响应
+    public static Result NO_LOGIN(){
+        return new Result(Constant.RESPONSE_CODE_NO_LOGIN, "未登录", null);
+    }
+    public static Result NO_LOGIN(String message){
+        return new Result(Constant.RESPONSE_CODE_NO_LOGIN, message, null);
+    }
+
+    //权限不足响应
+    public static Result FORBIDDEN(){
+        return new Result(Constant.RESPONSE_CODE_FORBIDDEN, "权限不足,禁止访问", null);
+    }
+}

+ 112 - 0
src/main/java/com/futu/goose/common/util/TokenUtils.java

@@ -0,0 +1,112 @@
+package com.futu.goose.common.util;
+
+import com.futu.goose.common.exception.RRException;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.JwtBuilder;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+import java.security.Key;
+import java.util.Date;
+
+/**
+ * 生成Token工具类
+ */
+public class TokenUtils {
+
+    /**
+     * 签名秘钥
+     */
+    public static final String SECRET = "cjyfutu1688";
+
+    /**
+     * 生成token
+     * @param id 一般传入userName
+     * @return
+     */
+    public static String createJwtToken(String id){
+        String issuer = "www.futureading.com";
+        String subject = "65532781@qq.com";
+        long ttlMillis = System.currentTimeMillis();
+        return createJwtToken(id, issuer, subject, ttlMillis);
+    }
+
+    /**
+     * 生成Token
+     *
+     * @param id
+     *            编号
+     * @param issuer
+     *            该JWT的签发者,是否使用是可选的
+     * @param subject
+     *            该JWT所面向的用户,是否使用是可选的;
+     * @param ttlMillis
+     *            签发时间
+     * @return token String
+     */
+    public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {
+
+        // 签名算法 ,将对token进行签名
+        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
+
+        // 生成签发时间
+        long nowMillis = System.currentTimeMillis();
+        Date now = new Date(nowMillis);
+
+        // 通过秘钥签名JWT
+        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
+        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
+
+        // Let's set the JWT Claims
+        JwtBuilder builder = Jwts.builder().setId(id)
+                .setIssuedAt(now)
+                .setSubject(subject)
+                .setIssuer(issuer)
+                .signWith(signatureAlgorithm, signingKey);
+
+        // if it has been specified, let's add the expiration
+        if (ttlMillis >= 0) {
+            long expMillis = nowMillis + ttlMillis;
+            Date exp = new Date(expMillis);
+            builder.setExpiration(exp);
+        }
+
+        // Builds the JWT and serializes it to a compact, URL-safe string
+        return builder.compact();
+
+    }
+
+    // Sample method to validate and read the JWT
+    public static Claims parseJWT(String jwt) {
+        // This line will throw an exception if it is not a signed JWS (as expected)
+        Claims claims = Jwts.parser()
+                .setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
+                .parseClaimsJws(jwt).getBody();
+        return claims;
+    }
+
+    public static Long getUserId(String token){
+        Claims claims = null;
+        try {
+            claims = TokenUtils.parseJWT(token);
+        } catch (Exception e) {
+            throw new RRException("token错误,请重新登录",101);
+        }
+
+        if(null==claims) {
+            throw new RRException("token错误,请重新登录",101);
+        }
+        String id = claims.getId();
+        Long userId=Long.valueOf(id);
+
+        return userId;
+
+
+    }
+
+    public static void main(String[] args) {
+        System.out.println(TokenUtils.createJwtToken("admin"));
+    }
+}

+ 34 - 0
src/main/java/com/futu/goose/common/util/WxService.java

@@ -0,0 +1,34 @@
+package com.futu.goose.common.util;
+
+import cn.hutool.core.codec.Base64;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.futu.goose.user.config.RedisKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.spec.AlgorithmParameterSpec;
+
+@Component
+public class WxService {
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+    public String  wxDecrypt(String encryptedData,String sessionId,String vi) throws Exception {
+        //开始解密
+        String json = redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID + sessionId);
+        JSONObject jsonObject = JSON.parseObject(json);
+        String sessionKey = (String) jsonObject.get("session_key");
+        byte[] encData = Base64.decode(encryptedData);
+        byte[] iv = Base64.decode(vi);
+        byte[] key = Base64.decode(sessionKey);
+        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
+        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
+        cipher.init(Cipher.DECRYPT_MODE,keySpec,ivSpec);
+        return new String(cipher.doFinal(encData),"UTF-8");
+    }
+}

+ 16 - 0
src/main/java/com/futu/goose/user/config/RedisConfig.java

@@ -0,0 +1,16 @@
+package com.futu.goose.user.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+@Configuration
+public class RedisConfig {
+    @Bean
+    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory connectionFactory) {
+        StringRedisTemplate template = new StringRedisTemplate();
+        template.setConnectionFactory(connectionFactory);
+        return template;
+    }
+}

+ 6 - 0
src/main/java/com/futu/goose/user/config/RedisKey.java

@@ -0,0 +1,6 @@
+package com.futu.goose.user.config;
+
+public class RedisKey {
+    public static final String WX_SESSION_ID = "wx_session_id_";
+    public static final String USER_TOKEN = "user_token";
+}

+ 38 - 0
src/main/java/com/futu/goose/user/config/RedissionConfig.java

@@ -0,0 +1,38 @@
+package com.futu.goose.user.config;
+
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class RedissionConfig {
+    @Value("${spring.redis.host}")
+    private String host;
+    @Value("${spring.redis.port}")
+    private Integer port;
+    @Value("${spring.redis.database}")
+    private Integer database;
+    @Value("${spring.redis.password}")
+    private String password;
+
+    @Bean
+    public RedissonClient getRedisson(){
+        Config config = new Config();
+        config.useSingleServer().setAddress("redis://" + host + ":" + port)
+                .setDatabase(database);
+        if (StringUtils.isNotEmpty(password)){
+            config.useSingleServer().setAddress("redis://" + host + ":" + port).setDatabase(database)
+                    .setPassword(password);
+        }else{
+            config.useSingleServer().setAddress("redis://" + host + ":" + port).setDatabase(database);
+        }
+        //设置全局默认看门狗机制续期时间,如果在使用时不设置,则使用全局的,如果全局不设置,则使用默认的30000,单位毫秒
+        config.setLockWatchdogTimeout(2000);
+        return Redisson.create(config);
+    }
+
+}

+ 4 - 0
src/main/java/com/futu/goose/user/controller/UserController.java

@@ -0,0 +1,4 @@
+package com.futu.goose.user.controller;
+
+public class UserController {
+}

+ 29 - 0
src/main/java/com/futu/goose/user/controller/WeChatController.java

@@ -0,0 +1,29 @@
+package com.futu.goose.user.controller;
+
+import com.futu.goose.user.dto.WXAuth;
+import com.futu.goose.user.service.impl.WechatUsersServiceImpl;
+import com.futu.goose.common.util.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/wx")
+public class WeChatController {
+
+    @Autowired
+    private WechatUsersServiceImpl wechatUsersService;
+
+    @GetMapping("/getSessionId")
+    public Result getSessionId(String code) {
+        if (code == null || code.trim().isEmpty()) {
+            return Result.ERROR("code不能为空");
+        }
+        return wechatUsersService.getSessionId(code);
+    }
+
+    @PostMapping("/authLogin")
+    public Result authLogin(@RequestBody @Validated WXAuth wxAuth) {
+        return wechatUsersService.authLogin(wxAuth);
+    }
+}

+ 20 - 0
src/main/java/com/futu/goose/user/dao/TUserMapper.java

@@ -0,0 +1,20 @@
+package com.futu.goose.user.dao;
+
+import com.futu.goose.user.domain.TUser;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+* @author 徐乐
+* @description 针对表【t_user(基础用户表)】的数据库操作Mapper
+* @createDate 2025-05-04 13:48:32
+* @Entity com.futu.goose.user.domain.TUser
+*/
+@Mapper
+public interface TUserMapper extends BaseMapper<TUser> {
+
+}
+
+
+
+

+ 20 - 0
src/main/java/com/futu/goose/user/dao/WechatUsersMapper.java

@@ -0,0 +1,20 @@
+package com.futu.goose.user.dao;
+
+import com.futu.goose.user.domain.WechatUsers;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+* @author 徐乐
+* @description 针对表【wechat_users(微信用户信息表)】的数据库操作Mapper
+* @createDate 2025-05-04 13:48:32
+* @Entity com.futu.goose.user.domain.WechatUsers
+*/
+@Mapper
+public interface WechatUsersMapper extends BaseMapper<WechatUsers> {
+
+}
+
+
+
+

+ 173 - 0
src/main/java/com/futu/goose/user/domain/TUser.java

@@ -0,0 +1,173 @@
+package com.futu.goose.user.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 基础用户表
+ * @TableName t_user
+ */
+@TableName(value ="t_user")
+@Data
+public class TUser implements Serializable {
+    /**
+     * 用户ID,主键
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 用户名
+     */
+    private String username;
+
+    /**
+     * 盐值
+     */
+    private String salt;
+
+    /**
+     * 密码(加密存储)
+     */
+    private String password;
+
+    /**
+     * 邮箱
+     */
+    private String email;
+
+    /**
+     * 手机号
+     */
+    private String phone;
+
+    /**
+     * 昵称
+     */
+    private String nickname;
+
+    /**
+     * 头像URL
+     */
+    private String avatar;
+
+    /**
+     * 性别(0-未知 1-男 2-女)
+     */
+    private Integer gender;
+
+    /**
+     * 生日
+     */
+    private Date birthday;
+
+    /**
+     * 状态(0-禁用 1-正常)
+     */
+    private Integer status;
+
+    /**
+     * 最后登录时间
+     */
+    private Date lastLoginTime;
+
+    /**
+     * 最后登录IP
+     */
+    private String lastLoginIp;
+
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updatedTime;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        TUser other = (TUser) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getUsername() == null ? other.getUsername() == null : this.getUsername().equals(other.getUsername()))
+            && (this.getSalt() == null ? other.getSalt() == null : this.getSalt().equals(other.getSalt()))
+            && (this.getPassword() == null ? other.getPassword() == null : this.getPassword().equals(other.getPassword()))
+            && (this.getEmail() == null ? other.getEmail() == null : this.getEmail().equals(other.getEmail()))
+            && (this.getPhone() == null ? other.getPhone() == null : this.getPhone().equals(other.getPhone()))
+            && (this.getNickname() == null ? other.getNickname() == null : this.getNickname().equals(other.getNickname()))
+            && (this.getAvatar() == null ? other.getAvatar() == null : this.getAvatar().equals(other.getAvatar()))
+            && (this.getGender() == null ? other.getGender() == null : this.getGender().equals(other.getGender()))
+            && (this.getBirthday() == null ? other.getBirthday() == null : this.getBirthday().equals(other.getBirthday()))
+            && (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()))
+            && (this.getLastLoginTime() == null ? other.getLastLoginTime() == null : this.getLastLoginTime().equals(other.getLastLoginTime()))
+            && (this.getLastLoginIp() == null ? other.getLastLoginIp() == null : this.getLastLoginIp().equals(other.getLastLoginIp()))
+            && (this.getCreatedTime() == null ? other.getCreatedTime() == null : this.getCreatedTime().equals(other.getCreatedTime()))
+            && (this.getUpdatedTime() == null ? other.getUpdatedTime() == null : this.getUpdatedTime().equals(other.getUpdatedTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getUsername() == null) ? 0 : getUsername().hashCode());
+        result = prime * result + ((getSalt() == null) ? 0 : getSalt().hashCode());
+        result = prime * result + ((getPassword() == null) ? 0 : getPassword().hashCode());
+        result = prime * result + ((getEmail() == null) ? 0 : getEmail().hashCode());
+        result = prime * result + ((getPhone() == null) ? 0 : getPhone().hashCode());
+        result = prime * result + ((getNickname() == null) ? 0 : getNickname().hashCode());
+        result = prime * result + ((getAvatar() == null) ? 0 : getAvatar().hashCode());
+        result = prime * result + ((getGender() == null) ? 0 : getGender().hashCode());
+        result = prime * result + ((getBirthday() == null) ? 0 : getBirthday().hashCode());
+        result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
+        result = prime * result + ((getLastLoginTime() == null) ? 0 : getLastLoginTime().hashCode());
+        result = prime * result + ((getLastLoginIp() == null) ? 0 : getLastLoginIp().hashCode());
+        result = prime * result + ((getCreatedTime() == null) ? 0 : getCreatedTime().hashCode());
+        result = prime * result + ((getUpdatedTime() == null) ? 0 : getUpdatedTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", username=").append(username);
+        sb.append(", salt=").append(salt);
+        sb.append(", password=").append(password);
+        sb.append(", email=").append(email);
+        sb.append(", phone=").append(phone);
+        sb.append(", nickname=").append(nickname);
+        sb.append(", avatar=").append(avatar);
+        sb.append(", gender=").append(gender);
+        sb.append(", birthday=").append(birthday);
+        sb.append(", status=").append(status);
+        sb.append(", lastLoginTime=").append(lastLoginTime);
+        sb.append(", lastLoginIp=").append(lastLoginIp);
+        sb.append(", createdTime=").append(createdTime);
+        sb.append(", updatedTime=").append(updatedTime);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 189 - 0
src/main/java/com/futu/goose/user/domain/WechatUsers.java

@@ -0,0 +1,189 @@
+package com.futu.goose.user.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.util.Date;
+import lombok.Data;
+
+/**
+ * 微信用户信息表
+ * @TableName wechat_users
+ */
+@TableName(value ="wechat_users")
+@Data
+public class WechatUsers implements Serializable {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 关联的用户ID
+     */
+    private Long userId;
+
+    /**
+     * 微信开放平台唯一标识
+     */
+    private String openid;
+
+    /**
+     * 微信联合ID(跨应用唯一)
+     */
+    private String unionid;
+
+    /**
+     * 微信昵称
+     */
+    private String nickname;
+
+    /**
+     * 微信头像URL
+     */
+    private String avatarUrl;
+
+    /**
+     * 性别(0-未知 1-男 2-女)
+     */
+    private Integer gender;
+
+    /**
+     * 国家
+     */
+    private String country;
+
+    /**
+     * 省份
+     */
+    private String province;
+
+    /**
+     * 城市
+     */
+    private String city;
+
+    /**
+     * 语言
+     */
+    private String language;
+
+    /**
+     * 小程序会话密钥
+     */
+    private String sessionKey;
+
+    /**
+     * 访问令牌
+     */
+    private String accessToken;
+
+    /**
+     * 刷新令牌
+     */
+    private String refreshToken;
+
+    /**
+     * 令牌过期时间(秒)
+     */
+    private Integer expiresIn;
+
+    /**
+     * 创建时间
+     */
+    private Date createdTime;
+
+    /**
+     * 更新时间
+     */
+    private Date updatedTime;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+
+    @Override
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        if (that == null) {
+            return false;
+        }
+        if (getClass() != that.getClass()) {
+            return false;
+        }
+        WechatUsers other = (WechatUsers) that;
+        return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
+            && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
+            && (this.getOpenid() == null ? other.getOpenid() == null : this.getOpenid().equals(other.getOpenid()))
+            && (this.getUnionid() == null ? other.getUnionid() == null : this.getUnionid().equals(other.getUnionid()))
+            && (this.getNickname() == null ? other.getNickname() == null : this.getNickname().equals(other.getNickname()))
+            && (this.getAvatarUrl() == null ? other.getAvatarUrl() == null : this.getAvatarUrl().equals(other.getAvatarUrl()))
+            && (this.getGender() == null ? other.getGender() == null : this.getGender().equals(other.getGender()))
+            && (this.getCountry() == null ? other.getCountry() == null : this.getCountry().equals(other.getCountry()))
+            && (this.getProvince() == null ? other.getProvince() == null : this.getProvince().equals(other.getProvince()))
+            && (this.getCity() == null ? other.getCity() == null : this.getCity().equals(other.getCity()))
+            && (this.getLanguage() == null ? other.getLanguage() == null : this.getLanguage().equals(other.getLanguage()))
+            && (this.getSessionKey() == null ? other.getSessionKey() == null : this.getSessionKey().equals(other.getSessionKey()))
+            && (this.getAccessToken() == null ? other.getAccessToken() == null : this.getAccessToken().equals(other.getAccessToken()))
+            && (this.getRefreshToken() == null ? other.getRefreshToken() == null : this.getRefreshToken().equals(other.getRefreshToken()))
+            && (this.getExpiresIn() == null ? other.getExpiresIn() == null : this.getExpiresIn().equals(other.getExpiresIn()))
+            && (this.getCreatedTime() == null ? other.getCreatedTime() == null : this.getCreatedTime().equals(other.getCreatedTime()))
+            && (this.getUpdatedTime() == null ? other.getUpdatedTime() == null : this.getUpdatedTime().equals(other.getUpdatedTime()));
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
+        result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
+        result = prime * result + ((getOpenid() == null) ? 0 : getOpenid().hashCode());
+        result = prime * result + ((getUnionid() == null) ? 0 : getUnionid().hashCode());
+        result = prime * result + ((getNickname() == null) ? 0 : getNickname().hashCode());
+        result = prime * result + ((getAvatarUrl() == null) ? 0 : getAvatarUrl().hashCode());
+        result = prime * result + ((getGender() == null) ? 0 : getGender().hashCode());
+        result = prime * result + ((getCountry() == null) ? 0 : getCountry().hashCode());
+        result = prime * result + ((getProvince() == null) ? 0 : getProvince().hashCode());
+        result = prime * result + ((getCity() == null) ? 0 : getCity().hashCode());
+        result = prime * result + ((getLanguage() == null) ? 0 : getLanguage().hashCode());
+        result = prime * result + ((getSessionKey() == null) ? 0 : getSessionKey().hashCode());
+        result = prime * result + ((getAccessToken() == null) ? 0 : getAccessToken().hashCode());
+        result = prime * result + ((getRefreshToken() == null) ? 0 : getRefreshToken().hashCode());
+        result = prime * result + ((getExpiresIn() == null) ? 0 : getExpiresIn().hashCode());
+        result = prime * result + ((getCreatedTime() == null) ? 0 : getCreatedTime().hashCode());
+        result = prime * result + ((getUpdatedTime() == null) ? 0 : getUpdatedTime().hashCode());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getSimpleName());
+        sb.append(" [");
+        sb.append("Hash = ").append(hashCode());
+        sb.append(", id=").append(id);
+        sb.append(", userId=").append(userId);
+        sb.append(", openid=").append(openid);
+        sb.append(", unionid=").append(unionid);
+        sb.append(", nickname=").append(nickname);
+        sb.append(", avatarUrl=").append(avatarUrl);
+        sb.append(", gender=").append(gender);
+        sb.append(", country=").append(country);
+        sb.append(", province=").append(province);
+        sb.append(", city=").append(city);
+        sb.append(", language=").append(language);
+        sb.append(", sessionKey=").append(sessionKey);
+        sb.append(", accessToken=").append(accessToken);
+        sb.append(", refreshToken=").append(refreshToken);
+        sb.append(", expiresIn=").append(expiresIn);
+        sb.append(", createdTime=").append(createdTime);
+        sb.append(", updatedTime=").append(updatedTime);
+        sb.append(", serialVersionUID=").append(serialVersionUID);
+        sb.append("]");
+        return sb.toString();
+    }
+}

+ 15 - 0
src/main/java/com/futu/goose/user/dto/WXAuth.java

@@ -0,0 +1,15 @@
+package com.futu.goose.user.dto;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+@Data
+public class WXAuth {
+    @NotBlank(message = "encryptedData不能为空")
+    private String encryptedData;
+    @NotBlank(message = "iv不能为空")
+    private String iv;
+    @NotBlank(message = "sessionId不能为空")
+    private String sessionId;
+}

+ 13 - 0
src/main/java/com/futu/goose/user/service/TUserService.java

@@ -0,0 +1,13 @@
+package com.futu.goose.user.service;
+
+import com.futu.goose.user.domain.TUser;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+* @author 徐乐
+* @description 针对表【t_user(基础用户表)】的数据库操作Service
+* @createDate 2025-05-04 13:48:32
+*/
+public interface TUserService extends IService<TUser> {
+
+}

+ 18 - 0
src/main/java/com/futu/goose/user/service/WechatUsersService.java

@@ -0,0 +1,18 @@
+package com.futu.goose.user.service;
+
+import com.futu.goose.user.domain.WechatUsers;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.futu.goose.user.dto.WXAuth;
+import com.futu.goose.common.util.Result;
+
+/**
+* @author 徐乐
+* @description 针对表【wechat_users(微信用户信息表)】的数据库操作Service
+* @createDate 2025-05-04 13:48:32
+*/
+public interface WechatUsersService extends IService<WechatUsers> {
+
+    Result getSessionId(String code);
+
+    Result authLogin(WXAuth wxAuth);
+}

+ 22 - 0
src/main/java/com/futu/goose/user/service/impl/TUserServiceImpl.java

@@ -0,0 +1,22 @@
+package com.futu.goose.user.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.futu.goose.user.domain.TUser;
+import com.futu.goose.user.service.TUserService;
+import com.futu.goose.user.dao.TUserMapper;
+import org.springframework.stereotype.Service;
+
+/**
+* @author 徐乐
+* @description 针对表【t_user(基础用户表)】的数据库操作Service实现
+* @createDate 2025-05-04 13:48:32
+*/
+@Service
+public class TUserServiceImpl extends ServiceImpl<TUserMapper, TUser>
+    implements TUserService{
+
+}
+
+
+
+

+ 165 - 0
src/main/java/com/futu/goose/user/service/impl/WechatUsersServiceImpl.java

@@ -0,0 +1,165 @@
+package com.futu.goose.user.service.impl;
+
+import cn.hutool.core.lang.Snowflake;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.futu.goose.common.util.TokenUtils;
+import com.futu.goose.user.config.RedisKey;
+import com.futu.goose.user.domain.WechatUsers;
+import com.futu.goose.user.domain.TUser;
+import com.futu.goose.user.dto.WXAuth;
+import com.futu.goose.user.service.WechatUsersService;
+import com.futu.goose.user.dao.WechatUsersMapper;
+import com.futu.goose.user.dao.TUserMapper;
+import com.futu.goose.common.util.Result;
+import com.futu.goose.common.util.WxService;
+import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+public class WechatUsersServiceImpl extends ServiceImpl<WechatUsersMapper, WechatUsers>
+        implements WechatUsersService {
+
+    @Value("${wechat.appid}")
+    private String appid;
+    @Value("${wechat.secret}")
+    private String secret;
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+    @Autowired
+    private WxService wxService;
+    @Autowired
+    private WechatUsersMapper wechatUsersMapper;
+    @Autowired
+    private TUserMapper userMapper;
+    @Autowired
+    private RedissonClient redissonClient;
+
+    @Override
+    public Result getSessionId(String code) {
+        //拼接一个url
+        String url = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
+        String replaceUrl = url.replace("{0}", appid).replace("{1}", secret).replace("{2}", code);
+        String res = HttpUtil.get(replaceUrl);
+        String uuid = UUID.randomUUID().toString();
+        redisTemplate.opsForValue().set(RedisKey.WX_SESSION_ID + uuid, res, 5, TimeUnit.MINUTES);
+        Map<String, String> map = new HashMap<>();
+        map.put("sessionId", uuid);
+        return Result.OK(map);
+    }
+
+
+    @Transactional
+    @Override
+    public Result authLogin(WXAuth wxAuth) {
+        try {
+            // 1. 从Redis获取session_key
+            String sessionInfo = redisTemplate.opsForValue().get(RedisKey.WX_SESSION_ID + wxAuth.getSessionId());
+            if (sessionInfo == null) {
+                return Result.ERROR("sessionId已过期,请重新登录");
+            }
+
+            Map<String, String> sessionMap = JSON.parseObject(sessionInfo, Map.class);
+            if (sessionMap == null) {
+                return Result.ERROR("无法解析session信息");
+            }
+            String openid = sessionMap.get("openid");
+            String unionid = sessionMap.get("unionid");
+            String sessionKey = sessionMap.get("session_key");
+            // 2. 解密用户信息(传入sessionKey而非sessionId)
+            String json = wxService.wxDecrypt(wxAuth.getEncryptedData(), wxAuth.getSessionId(), wxAuth.getIv());
+            if (json == null) {
+                return Result.ERROR("解密用户信息失败");
+            }
+
+            WechatUsers wechatUsers = JSON.parseObject(json, WechatUsers.class);
+
+            // 3. 检查微信用户是否存在
+            QueryWrapper<WechatUsers> wrapper = new QueryWrapper<>();
+            wrapper.eq("openid", openid);
+            //加锁
+            RLock lock = redissonClient.getLock(openid);
+            try {
+                boolean tryLock = lock.tryLock(3, TimeUnit.SECONDS);
+                if (!tryLock) {
+                    throw new RuntimeException("操作频繁,请稍后再试");
+                }
+                WechatUsers dbWechatUser = wechatUsersMapper.selectOne(wrapper);
+
+                TUser user;
+                if (dbWechatUser == null) {
+                    // 新用户注册
+                    Snowflake snowflake = IdUtil.getSnowflake();
+                    wechatUsers.setId(snowflake.nextId());
+                    wechatUsers.setOpenid(openid);
+                    wechatUsers.setUnionid(unionid);
+                    wechatUsers.setSessionKey(sessionKey);
+                    wechatUsersMapper.insert(wechatUsers);
+
+                    // 创建基础用户信息
+                    user = createNewUser(wechatUsers);
+                    wechatUsers.setUserId(user.getId());
+                    wechatUsersMapper.updateById(wechatUsers);
+                } else {
+                    // 登录
+                    user = userMapper.selectById(dbWechatUser.getUserId());
+                    if (user == null) {
+                        user = createNewUser(wechatUsers);
+                        dbWechatUser.setUserId(user.getId());
+                        wechatUsersMapper.updateById(dbWechatUser);
+                    }
+                }
+
+                // 4. 生成token
+                String token = TokenUtils.createJwtToken(user.getId().toString());
+                redisTemplate.opsForValue().set(RedisKey.USER_TOKEN + user.getId(), token, 7, TimeUnit.DAYS);
+
+                // 5. 返回结果
+                Map<String, Object> result = new HashMap<>();
+                result.put("token", token);
+                result.put("userInfo", user);
+                return Result.OK(result);
+            } catch (Exception e) {
+                e.printStackTrace();
+                return Result.ERROR("错误"+e);
+            } finally {
+                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
+                    lock.unlock(); //解锁
+                }
+            }
+        } catch (Exception e) {
+            log.error("微信登录失败: {}", e.getMessage(), e);
+            return Result.ERROR("登录失败: " + e.getMessage());
+        }
+    }
+
+    private TUser createNewUser(WechatUsers wechatUser) {
+        TUser user = new TUser();
+        Snowflake snowflake = IdUtil.getSnowflake();
+        user.setId(snowflake.nextId());
+        // 生成随机用户名(微信用户_+时间戳)
+        user.setUsername("wx_user_" + System.currentTimeMillis());
+        user.setNickname(wechatUser.getNickname());
+        user.setAvatar(wechatUser.getAvatarUrl());
+        user.setGender(wechatUser.getGender());
+        user.setStatus(1);
+        userMapper.insert(user);
+        return user;
+    }
+}

+ 24 - 0
target/classes/application.yml

@@ -0,0 +1,24 @@
+server:
+  port: 8080
+
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://47.111.97.7:3306/project?useSSL=false&serverTimezone=UTC&characterEncoding=UTF-8
+    username: root
+    password: root
+  redis:
+    host: 127.0.0.1
+    port: 6379
+    database: 0
+    password:
+mybatis-plus:
+  type-aliases-package: com.futu.goose.domain
+  configuration:
+    map-underscore-to-camel-case: true
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  mapper-locations: classpath:mapper/*.xml
+
+wechat:
+  appid: wxf463750c0be078db
+  secret: beba38298a2f51c6299703dc31744e12

BIN
target/classes/com/futu/goose/BigGooseApplication.class


BIN
target/classes/com/futu/goose/common/exception/RRException.class


BIN
target/classes/com/futu/goose/common/util/Constant.class


BIN
target/classes/com/futu/goose/common/util/Md5Util.class


BIN
target/classes/com/futu/goose/common/util/Result.class


BIN
target/classes/com/futu/goose/common/util/TokenUtils.class


BIN
target/classes/com/futu/goose/common/util/WxService.class


BIN
target/classes/com/futu/goose/course/controller/CourseController.class


BIN
target/classes/com/futu/goose/discounts/controller/DiscountsController.class


BIN
target/classes/com/futu/goose/message/controller/MessageController.class


BIN
target/classes/com/futu/goose/order/controller/OrderController.class


BIN
target/classes/com/futu/goose/pay/controller/PayController.class


BIN
target/classes/com/futu/goose/store/controller/StoreController.class


BIN
target/classes/com/futu/goose/system/controller/SystemController.class


BIN
target/classes/com/futu/goose/teacher/controller/TeacherController.class


BIN
target/classes/com/futu/goose/user/config/RedisConfig.class


BIN
target/classes/com/futu/goose/user/config/RedisKey.class


BIN
target/classes/com/futu/goose/user/config/RedissionConfig.class


BIN
target/classes/com/futu/goose/user/controller/UserController.class


BIN
target/classes/com/futu/goose/user/controller/WeChatController.class


BIN
target/classes/com/futu/goose/user/dao/TUserMapper.class


BIN
target/classes/com/futu/goose/user/dao/WechatUsersMapper.class


BIN
target/classes/com/futu/goose/user/domain/TUser.class


BIN
target/classes/com/futu/goose/user/domain/WechatUsers.class


BIN
target/classes/com/futu/goose/user/dto/WXAuth.class


BIN
target/classes/com/futu/goose/user/service/TUserService.class


BIN
target/classes/com/futu/goose/user/service/WechatUsersService.class


BIN
target/classes/com/futu/goose/user/service/impl/TUserServiceImpl.class


BIN
target/classes/com/futu/goose/user/service/impl/WechatUsersServiceImpl.class


+ 32 - 0
target/classes/mapper/TUserMapper.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.futu.goose.user.dao.TUserMapper">
+
+    <resultMap id="BaseResultMap" type="com.futu.goose.user.domain.TUser">
+            <id property="id" column="id" jdbcType="BIGINT"/>
+            <result property="username" column="username" jdbcType="VARCHAR"/>
+            <result property="salt" column="salt" jdbcType="VARCHAR"/>
+            <result property="password" column="password" jdbcType="VARCHAR"/>
+            <result property="email" column="email" jdbcType="VARCHAR"/>
+            <result property="phone" column="phone" jdbcType="VARCHAR"/>
+            <result property="nickname" column="nickname" jdbcType="VARCHAR"/>
+            <result property="avatar" column="avatar" jdbcType="VARCHAR"/>
+            <result property="gender" column="gender" jdbcType="TINYINT"/>
+            <result property="birthday" column="birthday" jdbcType="DATE"/>
+            <result property="status" column="status" jdbcType="TINYINT"/>
+            <result property="lastLoginTime" column="last_login_time" jdbcType="TIMESTAMP"/>
+            <result property="lastLoginIp" column="last_login_ip" jdbcType="VARCHAR"/>
+            <result property="createdTime" column="created_time" jdbcType="TIMESTAMP"/>
+            <result property="updatedTime" column="updated_time" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,username,salt,
+        password,email,phone,
+        nickname,avatar,gender,
+        birthday,status,last_login_time,
+        last_login_ip,created_time,updated_time
+    </sql>
+</mapper>

+ 35 - 0
target/classes/mapper/WechatUsersMapper.xml

@@ -0,0 +1,35 @@
+<?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.futu.goose.user.dao.WechatUsersMapper">
+
+    <resultMap id="BaseResultMap" type="com.futu.goose.user.domain.WechatUsers">
+            <id property="id" column="id" jdbcType="BIGINT"/>
+            <result property="userId" column="user_id" jdbcType="BIGINT"/>
+            <result property="openid" column="openid" jdbcType="VARCHAR"/>
+            <result property="unionid" column="unionid" jdbcType="VARCHAR"/>
+            <result property="nickname" column="nickname" jdbcType="VARCHAR"/>
+            <result property="avatarUrl" column="avatar_url" jdbcType="VARCHAR"/>
+            <result property="gender" column="gender" jdbcType="TINYINT"/>
+            <result property="country" column="country" jdbcType="VARCHAR"/>
+            <result property="province" column="province" jdbcType="VARCHAR"/>
+            <result property="city" column="city" jdbcType="VARCHAR"/>
+            <result property="language" column="language" jdbcType="VARCHAR"/>
+            <result property="sessionKey" column="session_key" jdbcType="VARCHAR"/>
+            <result property="accessToken" column="access_token" jdbcType="VARCHAR"/>
+            <result property="refreshToken" column="refresh_token" jdbcType="VARCHAR"/>
+            <result property="expiresIn" column="expires_in" jdbcType="INTEGER"/>
+            <result property="createdTime" column="created_time" jdbcType="TIMESTAMP"/>
+            <result property="updatedTime" column="updated_time" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,user_id,openid,
+        unionid,nickname,avatar_url,
+        gender,country,province,
+        city,language,session_key,
+        access_token,refresh_token,expires_in,
+        created_time,updated_time
+    </sql>
+</mapper>