From 7b05ee530ef5bc52045d58efba6a5aff970920ad Mon Sep 17 00:00:00 2001
From: admin <weikou2014>
Date: 星期四, 13 二月 2025 17:07:27 +0800
Subject: [PATCH] 系统基本功能完善,用用来做SpringBoot系统的基本框架(集成mybatis/redis/腾讯云对象存储/阿里云一键登录)

---
 src/main/java/com/everyday/word/config/CosFilePathEnum.java                      |   32 +
 src/main/java/com/everyday/word/service/impl/user/UserServiceImpl.java           |   30 
 src/main/resources/mapper/UserAuthMapper.xml                                     |    2 
 src/main/java/com/everyday/word/config/TencentCosConfig.java                     |   62 ++
 src/main/java/com/everyday/word/service/impl/EnglishWordsServiceImpl.java        |    2 
 src/main/java/com/everyday/word/controller/client/user/UserController.java       |  157 ++++++
 src/test/java/com/everyday/word/WordsTest.java                                   |    2 
 src/main/resources/application-pro.yml                                           |   39 +
 src/main/java/com/everyday/word/service/inter/SMSService.java                    |   39 +
 pom.xml                                                                          |    7 
 src/main/java/com/everyday/word/config/RedisConfig.java                          |  107 ++++
 src/main/java/com/everyday/word/controller/client/user/LoginController.java      |  202 +++++++
 src/main/resources/mapper/UserMapper.xml                                         |    2 
 src/main/java/com/everyday/word/service/inter/user/UserService.java              |   13 
 src/main/java/com/everyday/word/vo/user/UserInfoVO.java                          |   22 
 src/test/java/com/everyday/word/ConfigTest.java                                  |   47 +
 src/main/java/com/everyday/word/factory/user/UserFactory.java                    |   76 ++
 src/main/resources/application-dev.yml                                           |   34 +
 src/main/java/com/everyday/word/service/impl/SMSServiceImpl.java                 |  108 ++++
 src/main/java/com/everyday/word/entity/user/User.java                            |    7 
 src/test/java/com/everyday/word/CosTest.java                                     |   28 +
 src/main/java/com/everyday/word/service/impl/config/SystemConfigServiceImpl.java |    9 
 src/main/java/com/everyday/word/entity/config/SystemConfig.java                  |   18 
 src/main/java/com/everyday/word/service/inter/EnglishWordsService.java           |    2 
 src/main/resources/mapper/SystemConfigMapper.xml                                 |  163 +++--
 src/test/java/com/everyday/word/RedisTest.java                                   |   28 +
 src/main/resources/base.sql                                                      |   46 +
 /dev/null                                                                        |   41 -
 src/main/java/com/everyday/word/aspect/SignValidateAspect.java                   |   10 
 src/main/java/com/everyday/word/config/WebSecurityConfig.java                    |   20 
 src/main/java/com/everyday/word/service/impl/AdminUserServiceImpl.java           |    2 
 src/main/java/com/everyday/word/entity/config/SystemConfigKeyEnum.java           |   10 
 src/main/java/com/everyday/word/vo/AcceptData.java                               |    4 
 src/main/java/com/everyday/word/service/RedisManager.java                        |   85 +++
 src/main/java/com/everyday/word/entity/user/UserAuth.java                        |   12 
 src/main/java/com/everyday/word/service/inter/AdminUserService.java              |    2 
 src/main/java/com/everyday/word/config/RedisKeyEnum.java                         |   34 +
 src/main/java/com/everyday/word/utils/AliyunOneKeyLoginUtil.java                 |    3 
 38 files changed, 1,350 insertions(+), 157 deletions(-)

diff --git a/pom.xml b/pom.xml
index d597d4d..84ec200 100644
--- a/pom.xml
+++ b/pom.xml
@@ -230,6 +230,8 @@
             </exclusions>
         </dependency>
 
+
+
         <dependency>
             <groupId>com.aliyun</groupId>
             <artifactId>aliyun-java-sdk-core</artifactId>
@@ -287,6 +289,11 @@
             <version>1.9.2</version>
         </dependency>
 
+        <!-- Redis -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
 
 
     </dependencies>
diff --git a/src/main/java/com/everyday/word/aspect/SignValidateAspect.java b/src/main/java/com/everyday/word/aspect/SignValidateAspect.java
index 7851b5b..0fbe7b3 100644
--- a/src/main/java/com/everyday/word/aspect/SignValidateAspect.java
+++ b/src/main/java/com/everyday/word/aspect/SignValidateAspect.java
@@ -34,11 +34,9 @@
     @Around(EDP)
     public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
         Object[] args = joinPoint.getArgs();
-        PrintWriter out = null;
         ServletRequestAttributes servletContainer = (ServletRequestAttributes) RequestContextHolder
                 .getRequestAttributes();
 
-        out = servletContainer.getResponse().getWriter();
         HttpServletRequest request = servletContainer.getRequest();
 
         AcceptData acceptData = null;
@@ -50,7 +48,7 @@
             }
         }
         if (acceptData == null) {
-            out.print(JsonUtil.loadFalseResult(-1, "绛惧悕閿欒"));
+            servletContainer.getResponse().getWriter().print(JsonUtil.loadFalseResult(-1, "绛惧悕閿欒"));
             return null;
         }
 
@@ -65,20 +63,19 @@
                 JSONObject data = new JSONObject();
                 data.put("code", -2);
                 data.put("msg", "鏃堕棿閿欒");
+                PrintWriter out = servletContainer.getResponse().getWriter();
                 out.print(data);
                 out.close();
                 return null;
             }
-            final String url = request.getRequestURI();
 
             Object obj = null;
             try {
-                long startTime = System.currentTimeMillis();
                 obj = joinPoint.proceed(args);
-                final long responseTime = System.currentTimeMillis() - startTime;
                 // 璁板綍澶т簬2s鐨勮姹�
             } catch (Throwable e) {
                 if (!Constant.IS_TEST) {
+                    PrintWriter out = servletContainer.getResponse().getWriter();
                     out.print(JsonUtil.loadFalseResult(90009, "鏈嶅姟鍣ㄥ唴閮ㄩ敊璇�"));
                 } else {
                     throw e;
@@ -86,6 +83,7 @@
             }
             return obj;
         } else {
+            PrintWriter out = servletContainer.getResponse().getWriter();
             out.print(JsonUtil.loadFalseResult(-1, "绛惧悕閿欒"));
             out.close();
             return null;
diff --git a/src/main/java/com/everyday/word/config/CosFilePathEnum.java b/src/main/java/com/everyday/word/config/CosFilePathEnum.java
new file mode 100644
index 0000000..197f294
--- /dev/null
+++ b/src/main/java/com/everyday/word/config/CosFilePathEnum.java
@@ -0,0 +1,32 @@
+package com.everyday.word.config;
+
+/**
+ * @author hxh
+ * @title: CosFilePathEnum
+ * @description: 鑵捐浜慶os璺緞
+ * @date 2025/2/13 10:21
+ */
+public enum CosFilePathEnum {
+    // 鐢ㄦ埛鐩稿叧
+    userPortrait("/user/img/portrait/", "鐢ㄦ埛澶村儚");
+    private final String path;
+    private final String desc;
+
+    private CosFilePathEnum(String path, String desc) {
+        this.path = path;
+        this.desc = desc;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public String getWholePath(String subPath) {
+        return path + subPath;
+    }
+
+}
diff --git a/src/main/java/com/everyday/word/config/RedisConfig.java b/src/main/java/com/everyday/word/config/RedisConfig.java
new file mode 100644
index 0000000..e61180c
--- /dev/null
+++ b/src/main/java/com/everyday/word/config/RedisConfig.java
@@ -0,0 +1,107 @@
+package com.everyday.word.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisPassword;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.time.Duration;
+
+/**
+ * @author hxh
+ * @title: RedisConfig
+ * @description: Redis閰嶇疆
+ * @date 2025/2/12 16:10
+ */
+@Configuration
+public class RedisConfig extends CachingConfigurerSupport {
+
+    @Value("${spring.redis.host}")
+    private String addr;
+    @Value("${spring.redis.port}")
+    private int port;
+    @Value("${spring.redis.timeout}")
+    private int timeout;
+    @Value("${spring.redis.password}")
+    private String auth;
+    @Value("${spring.redis.database}")
+    private int database;
+    @Value("${spring.redis.lettuce.pool.max-active}")
+    private int maxActive;
+    @Value("${spring.redis.lettuce.pool.max-wait}")
+    private int maxWait;
+    @Value("${spring.redis.lettuce.pool.max-idle}")
+    private int maxIdle;
+    @Value("${spring.redis.lettuce.pool.min-idle}")
+    private int minIdle;
+    @Value("${spring.redis.lettuce.shutdown-timeout}")
+    private int shutDownTimeout;
+
+
+    @Bean
+    public LettuceConnectionFactory lettuceConnectionFactory() {
+        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
+        genericObjectPoolConfig.setMaxIdle(maxIdle);
+        genericObjectPoolConfig.setMinIdle(minIdle);
+        genericObjectPoolConfig.setMaxTotal(maxActive);
+        genericObjectPoolConfig.setMaxWaitMillis(maxWait);
+        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(1000);
+        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
+        redisStandaloneConfiguration.setDatabase(database);
+        redisStandaloneConfiguration.setHostName(addr);
+        redisStandaloneConfiguration.setPort(port);
+        redisStandaloneConfiguration.setPassword(RedisPassword.of(auth));
+        LettuceClientConfiguration clientConfig = LettucePoolingClientConfiguration.builder()
+                .commandTimeout(Duration.ofMillis(timeout))
+                .shutdownTimeout(Duration.ofMillis(shutDownTimeout))
+                .poolConfig(genericObjectPoolConfig)
+                .build();
+
+        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisStandaloneConfiguration, clientConfig);
+        //鍏佽澶氫釜杩炴帴鍏敤涓�涓墿鐞嗚繛鎺ワ紝榛樿涓簍rue
+//        factory.setShareNativeConnection(true);
+// 妫�楠岄摼鎺ユ槸鍚﹀彲鐢紝榛樿涓篺alse
+        factory.setValidateConnection(true);
+        return factory;
+    }
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
+        RedisTemplate<String, Object> template = new RedisTemplate<>();
+        template.setConnectionFactory(lettuceConnectionFactory);
+        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
+        ObjectMapper om = new ObjectMapper();
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
+                ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
+        jackson2JsonRedisSerializer.setObjectMapper(om);
+
+
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        // key閲囩敤String鐨勫簭鍒楀寲鏂瑰紡
+        template.setKeySerializer(stringRedisSerializer);
+        // hash鐨刱ey涔熼噰鐢⊿tring鐨勫簭鍒楀寲鏂瑰紡
+        template.setHashKeySerializer(stringRedisSerializer);
+        // value搴忓垪鍖栨柟寮忛噰鐢╦ackson
+        template.setValueSerializer(jackson2JsonRedisSerializer);
+        // hash鐨剉alue搴忓垪鍖栨柟寮忛噰鐢╦ackson
+        template.setHashValueSerializer(jackson2JsonRedisSerializer);
+        template.afterPropertiesSet();
+        return template;
+    }
+
+}
diff --git a/src/main/java/com/everyday/word/config/RedisKeyEnum.java b/src/main/java/com/everyday/word/config/RedisKeyEnum.java
new file mode 100644
index 0000000..b283395
--- /dev/null
+++ b/src/main/java/com/everyday/word/config/RedisKeyEnum.java
@@ -0,0 +1,34 @@
+package com.everyday.word.config;
+
+/**
+ * @author hxh
+ * @title: RedisKeyEnum
+ * @description: Redis鐨勯敭鏋氫妇
+ * @date 2025/2/11 17:08
+ */
+public enum RedisKeyEnum {
+    emptyKey("", "绌哄��-key澶栭儴绌哄��"),
+    SMS("sms-", "鐭俊"),
+    SMSVCode("smscode-", "鐭俊楠岃瘉鐮�");
+
+    private final String key;
+    private final String desc;
+
+    private RedisKeyEnum(String key, String desc) {
+        this.key = key;
+        this.desc = desc;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public static String getRedisKey(RedisKeyEnum keyEnum, String value) {
+        return keyEnum.getKey() + value;
+    }
+
+}
diff --git a/src/main/java/com/everyday/word/config/TencentCosConfig.java b/src/main/java/com/everyday/word/config/TencentCosConfig.java
new file mode 100644
index 0000000..baf8499
--- /dev/null
+++ b/src/main/java/com/everyday/word/config/TencentCosConfig.java
@@ -0,0 +1,62 @@
+package com.everyday.word.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisPassword;
+import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
+import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
+import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.yeshi.utils.tencentcloud.COSManager;
+import org.yeshi.utils.tencentcloud.entity.COSInitParams;
+
+import java.time.Duration;
+
+/**
+ * @author hxh
+ * @title: RedisConfig
+ * @description: 鑵捐浜慍OS閰嶇疆
+ * @date 2025/2/12 16:10
+ */
+@Configuration
+public class TencentCosConfig extends CachingConfigurerSupport {
+
+    @Value("${qcloud.cos.appid}")
+    private long appId;
+    @Value("${qcloud.cos.secret_id}")
+    private String secretId;
+    @Value("${qcloud.cos.secret_key}")
+    private String secretKey;
+    @Value("${qcloud.cos.bucket_name}")
+    private String bucketName;
+    @Value("${qcloud.cos.region}")
+    private String region;
+    @Value("${qcloud.cos.access_host}")
+    private String accessHost;
+
+
+    @Bean
+    public COSManager createCosManager() {
+        COSInitParams params=new COSInitParams();
+        params.setAppId(appId);
+        params.setSecretId(secretId);
+        params.setSecretKey(secretKey);
+        params.setBucketName(bucketName);
+        params.setRegion(region);
+        params.setAccessHost(accessHost);
+        COSManager.getInstance().init(params);
+        return COSManager.getInstance();
+    }
+
+}
diff --git a/src/main/java/com/everyday/word/config/WebSecurityConfig.java b/src/main/java/com/everyday/word/config/WebSecurityConfig.java
index b6ee039..780c85c 100644
--- a/src/main/java/com/everyday/word/config/WebSecurityConfig.java
+++ b/src/main/java/com/everyday/word/config/WebSecurityConfig.java
@@ -2,7 +2,7 @@
 
 import com.everyday.word.entity.AdminUser;
 import com.everyday.word.exception.VerificationCodeException;
-import com.everyday.word.service.AdminUserService;
+import com.everyday.word.service.inter.AdminUserService;
 import com.google.code.kaptcha.Producer;
 import com.google.code.kaptcha.impl.DefaultKaptcha;
 import com.google.code.kaptcha.util.Config;
@@ -58,6 +58,10 @@
 
     private final static String[] STATIC_RESOURCE_PATHS = new String[]{
             "/*.html", "/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg", "/**/*.gif", "/**/*.xml", "/**/font/*", "/**/fonts/*", "/**/layui/**"
+    };
+
+    private final static String[] PERMIT_URIS = new String[]{
+            "/admin/api/captcha.jpg*", "/api/**"
     };
 
 
@@ -131,6 +135,7 @@
                 //闈炵櫥褰曟帴鍙�
                 String url = httpServletRequest.getRequestURI();
                 //涓嶉獙璇侀潤鎬佽祫婧�
+                System.out.println("杩囨护閾炬帴锛�"+url);
                 AntPathMatcher pathMatcher = new AntPathMatcher();
                 for (String resource : STATIC_RESOURCE_PATHS) {
                     if (pathMatcher.match(resource, url)) {
@@ -199,7 +204,7 @@
         http.headers().frameOptions().disable();
         http.authorizeRequests()
                 // 閰嶇疆涓嶉渶瑕侀壌鏉冪殑鎺ュ彛
-                .antMatchers("/admin/api/captcha.jpg*", "/api/**", "/webapi/**","*/agentapi/**").permitAll()
+                .antMatchers(PERMIT_URIS).permitAll()
                 //閰嶇疆闇�瑕侀壌鏉冪殑鎺ュ彛
                 .antMatchers("/admin/api/**", "/admin/index.html").authenticated()
                 .and()
@@ -246,10 +251,8 @@
                 .csrf().disable()
                 .rememberMe().userDetailsService(new MyUserDetailsService())
                 .and().exceptionHandling().authenticationEntryPoint(new NotLoginAuthenticationEntryPoint());
+        // TODO 鏆傛椂涓嶉獙璇�
         http.addFilterBefore(new PreRequestVerifyFilter(), UsernamePasswordAuthenticationFilter.class);
-
-
-        // http.addFilterBefore(new PreRequestVerifyFilter(), UsernamePasswordAuthenticationFilter.class);
     }
 
 
@@ -328,5 +331,12 @@
         }
     }
 
+    public static void main(String[] args) {
+
+      boolean match =  new AntPathMatcher().match("/api/**","/api/user/login/wx");
+      System.out.println(match);
+
+    }
+
 
 }
diff --git a/src/main/java/com/everyday/word/controller/client/user/LoginController.java b/src/main/java/com/everyday/word/controller/client/user/LoginController.java
new file mode 100644
index 0000000..639857e
--- /dev/null
+++ b/src/main/java/com/everyday/word/controller/client/user/LoginController.java
@@ -0,0 +1,202 @@
+package com.everyday.word.controller.client.user;
+
+import com.everyday.word.dto.QQUserInfo;
+import com.everyday.word.entity.SystemEnum;
+import com.everyday.word.entity.config.SystemConfigKeyEnum;
+import com.everyday.word.entity.user.IdentityType;
+import com.everyday.word.entity.user.User;
+import com.everyday.word.entity.user.UserAuth;
+import com.everyday.word.exception.SMSException;
+import com.everyday.word.factory.user.UserFactory;
+import com.everyday.word.service.inter.SMSService;
+import com.everyday.word.service.inter.config.SystemConfigService;
+import com.everyday.word.service.inter.user.UserService;
+import com.everyday.word.utils.AliyunOneKeyLoginUtil;
+import com.everyday.word.vo.AcceptData;
+import com.everyday.word.vo.user.UserInfoVO;
+import com.google.gson.Gson;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.yeshi.utils.JsonUtil;
+import org.yeshi.utils.StringUtil;
+import org.yeshi.utils.entity.wx.WeiXinUser;
+import org.yeshi.utils.wx.WXAppLoginUtil;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpSession;
+import java.util.List;
+
+/**
+ * @author hxh
+ * @title: UserController
+ * @description:
+ * @date 2025/2/7 15:59
+ */
+@Controller
+@RequestMapping("api/user/login")
+public class LoginController {
+
+    @Resource
+    private SMSService smsService;
+
+    @Resource
+    private UserService userService;
+
+    @Resource
+    private SystemConfigService systemConfigService;
+
+    private final Gson gson = JsonUtil.getSimpleGson();
+
+    private UserInfoVO getUserInfoVO(Long uid) {
+        User user = userService.selectUser(uid);
+        if (user == null) {
+            return null;
+        }
+        List<UserAuth> authList = userService.listUserAuth(uid);
+        return UserFactory.createVO(user, authList);
+    }
+
+
+    private User loginByPhone(String phone, SystemEnum system) {
+        User user = userService.loginByPhone(phone, system);
+        if (user == null) {
+            // 娉ㄥ唽
+            userService.register(UserFactory.createAuth(phone, system));
+        }
+        user = userService.loginByPhone(phone, system);
+        return user;
+    }
+
+    /**
+     * @return java.lang.String
+     * @author hxh
+     * @description 鐢佃瘽鍙风爜鐧诲綍
+     * @date 11:28 2025/2/13
+     * @param: acceptData
+     * @param: phone
+     * @param: vcode
+     * @param: accessToken 涓�閿櫥褰曠殑Token
+     * @param: session
+     **/
+    @ResponseBody
+    @RequestMapping("phone")
+    public String phoneLogin(AcceptData acceptData, String phone, String vcode, HttpSession session) {
+        // 鐢佃瘽鍙风爜鐧诲綍
+        if (StringUtil.isNullOrEmpty(phone)) {
+            return JsonUtil.loadFalseResult("鐢佃瘽鍙风爜涓嶈兘涓虹┖");
+        }
+        if (StringUtil.isNullOrEmpty(vcode)) {
+            return JsonUtil.loadFalseResult("楠岃瘉鐮佷笉鑳戒负绌�");
+        }
+        // 楠岃瘉楠岃瘉鐮�
+        String cacheCode = smsService.getVerifyCode(acceptData.getSystem(), phone, 1);
+        if (!vcode.equalsIgnoreCase(cacheCode)) {
+            return JsonUtil.loadFalseResult("楠岃瘉鐮侀敊璇�");
+        }
+
+        User user = loginByPhone(phone, acceptData.getSystem());
+        if (user == null) {
+            return JsonUtil.loadFalseResult("鐧诲綍澶辫触");
+        }
+        return JsonUtil.loadTrueResult(gson.toJson(getUserInfoVO(user.getId())));
+    }
+
+    @ResponseBody
+    @RequestMapping("sendVerifyCode")
+    public String sendSms(AcceptData acceptData, String phone, Long uid, HttpSession session) {
+        try {
+            if (phone.contains("**") && uid != null && uid > 0) {
+                UserAuth userAuth = userService.selectUserAuth(uid, IdentityType.PHONE);
+                if (userAuth == null) {
+                    return JsonUtil.loadFalseResult(2, "鐢ㄦ埛涓嶅瓨鍦�/鏈粦瀹氭墜鏈哄彿");
+                }
+                phone = userAuth.getIdentifier();
+            }
+
+            if (!StringUtil.isMobile(phone)) {
+                return JsonUtil.loadFalseResult(4, "鐢佃瘽鍙风爜鏍煎紡涓嶆纭�");
+            }
+            smsService.sendVerifyCode(acceptData.getSystem(), phone, 6, 1);
+            return JsonUtil.loadTrueResult("鍙戦�佹垚鍔�");
+        } catch (SMSException e) {
+            return JsonUtil.loadFalseResult(e.getCode(), e.getMsg());
+        }
+    }
+
+
+    /**
+     * @return java.lang.String
+     * @author hxh
+     * @description 涓�閿櫥褰�
+     * @date 16:09 2025/2/7
+     * @param: acceptData
+     * @param: token
+     * @param: session
+     **/
+    @ResponseBody
+    @RequestMapping("one_key")
+    public String oneKeyLogin(AcceptData acceptData, String token, HttpSession session) {
+        String phone = AliyunOneKeyLoginUtil.getMobile(token, "");
+        if (StringUtil.isNullOrEmpty(phone)) {
+            return JsonUtil.loadFalseResult("鎵嬫満鍙疯幏鍙栧け璐�");
+        }
+        User user = loginByPhone(phone, acceptData.getSystem());
+        if (user == null) {
+            return JsonUtil.loadFalseResult("鐧诲綍澶辫触");
+        }
+        return JsonUtil.loadTrueResult(gson.toJson(getUserInfoVO(user.getId())));
+    }
+
+
+    private WeiXinUser getWeiXinUserByCode(String code, SystemEnum system) {
+        String wxAppId = systemConfigService.getValueCache(SystemConfigKeyEnum.wxAppId, system);
+        String wxAppSecret = systemConfigService.getValueCache(SystemConfigKeyEnum.wxAppSecret, system);
+        if (StringUtil.isNullOrEmpty(wxAppId) || StringUtil.isNullOrEmpty(wxAppSecret)) {
+            return null;
+        }
+        return WXAppLoginUtil.getWeiXinUser(code, wxAppId, wxAppSecret);
+    }
+
+
+    @ResponseBody
+    @RequestMapping("wx")
+    public String wxLogin(AcceptData acceptData, String code, HttpSession session) {
+        if (StringUtil.isNullOrEmpty(code)) {
+            return JsonUtil.loadFalseResult("寰俊鎺堟潈鐮佹湭涓婁紶");
+        }
+        WeiXinUser weiXinUser = getWeiXinUserByCode(code, acceptData.getSystem());
+        if (weiXinUser == null) {
+            return JsonUtil.loadFalseResult("寰俊鎺堟潈澶辫触");
+        }
+        User user = userService.loginByWX(weiXinUser, acceptData.getSystem());
+        if (user == null) {
+            userService.register(UserFactory.createAuth(weiXinUser, acceptData.getSystem()));
+            user = userService.loginByWX(weiXinUser, acceptData.getSystem());
+        }
+        if (user == null) {
+            return JsonUtil.loadFalseResult("鐧诲綍澶辫触");
+        }
+        return JsonUtil.loadTrueResult(gson.toJson(getUserInfoVO(user.getId())));
+    }
+
+    @ResponseBody
+    @RequestMapping("qq")
+    public String qqLogin(AcceptData acceptData, QQUserInfo qqUserInfo, HttpSession session) {
+        if (StringUtil.isNullOrEmpty(qqUserInfo.getOpenid())) {
+            return JsonUtil.loadFalseResult("QQ鎺堟潈鐮佹湭涓婁紶");
+        }
+        UserAuth userAuth = UserFactory.createAuth(qqUserInfo, acceptData.getSystem());
+        User user = userService.loginByQQ(qqUserInfo.getOpenid(), acceptData.getSystem());
+        if (user == null) {
+            userService.register(userAuth);
+            user = userService.loginByQQ(qqUserInfo.getOpenid(), acceptData.getSystem());
+        }
+        if (user == null) {
+            return JsonUtil.loadFalseResult("鐧诲綍澶辫触");
+        }
+        return JsonUtil.loadTrueResult(gson.toJson(getUserInfoVO(user.getId())));
+    }
+
+
+}
diff --git a/src/main/java/com/everyday/word/controller/client/user/UserController.java b/src/main/java/com/everyday/word/controller/client/user/UserController.java
new file mode 100644
index 0000000..8229bf0
--- /dev/null
+++ b/src/main/java/com/everyday/word/controller/client/user/UserController.java
@@ -0,0 +1,157 @@
+package com.everyday.word.controller.client.user;
+
+import com.everyday.word.dto.QQUserInfo;
+import com.everyday.word.entity.config.SystemConfigKeyEnum;
+import com.everyday.word.entity.user.User;
+import com.everyday.word.entity.user.UserAuth;
+import com.everyday.word.factory.user.UserFactory;
+import com.everyday.word.service.inter.SMSService;
+import com.everyday.word.service.inter.config.SystemConfigService;
+import com.everyday.word.service.inter.user.UserService;
+import com.everyday.word.utils.AliyunOneKeyLoginUtil;
+import com.everyday.word.vo.AcceptData;
+import com.everyday.word.vo.user.UserInfoVO;
+import com.google.gson.Gson;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import org.yeshi.utils.JsonUtil;
+import org.yeshi.utils.StringUtil;
+import org.yeshi.utils.entity.wx.WeiXinUser;
+import org.yeshi.utils.wx.WXAppLoginUtil;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author hxh
+ * @title: UserController
+ * @description:
+ * @date 2025/2/7 15:59
+ */
+@Controller
+@RequestMapping("user")
+public class UserController {
+
+    @Resource
+    private SMSService smsService;
+
+    @Resource
+    private UserService userService;
+
+    @Resource
+    private SystemConfigService systemConfigService;
+
+    private final Gson gson = JsonUtil.getSimpleGson();
+
+
+    @ResponseBody
+    @RequestMapping("uploadPortrait")
+    public String uploadPortrait(AcceptData acceptData, MultipartFile portrait) {
+        if (acceptData.getUid() == null) {
+            return JsonUtil.loadFalseResult("鐢ㄦ埛鏈櫥褰�");
+        }
+        if (portrait == null) {
+            return JsonUtil.loadFalseResult("涓婁紶鏂囦欢涓嶈兘涓虹┖");
+        }
+
+        try {
+            userService.uploadPortrait(portrait, acceptData.getUid());
+            return JsonUtil.loadTrueResult("");
+        } catch (IOException e) {
+            return JsonUtil.loadFalseResult("澶村儚涓婁紶澶辫触");
+        }
+    }
+
+
+    @ResponseBody
+    @RequestMapping("getUserInfo")
+    public String getUserInfo(AcceptData acceptData) {
+        if (acceptData.getUid() == null) {
+            return JsonUtil.loadFalseResult("鐢ㄦ埛鏈櫥褰�");
+        }
+        User user = userService.selectUser(acceptData.getUid());
+        if (user == null) {
+            return JsonUtil.loadFalseResult("灏氭湭鑾峰彇鍒扮敤鎴蜂俊鎭�");
+        }
+        List<UserAuth> authList = userService.listUserAuth(acceptData.getUid());
+        UserInfoVO vo = UserFactory.createVO(user, authList);
+        return JsonUtil.loadTrueResult(gson.toJson(vo));
+    }
+
+
+    /**
+     * @return java.lang.String
+     * @author hxh
+     * @description 缁戝畾鐢佃瘽
+     * @date 14:37 2025/2/13
+     * @param: acceptData
+     * @param: phone
+     * @param: vcode
+     * @param: token
+     **/
+    @ResponseBody
+    @RequestMapping("bindPhone")
+    public String bindPhone(AcceptData acceptData, String phone, String vcode, String token) {
+        if (acceptData.getUid() == null) {
+            return JsonUtil.loadFalseResult("鐢ㄦ埛鏈櫥褰�");
+        }
+        if (!StringUtil.isNullOrEmpty(token)) {
+            if (StringUtil.isNullOrEmpty(phone)) {
+                return JsonUtil.loadFalseResult("鐢佃瘽鍙风爜涓嶈兘涓虹┖");
+            }
+            if (StringUtil.isNullOrEmpty(vcode)) {
+                return JsonUtil.loadFalseResult("楠岃瘉鐮佷笉鑳戒负绌�");
+            }
+            // 楠岃瘉楠岃瘉鐮�
+            String cacheCode = smsService.getVerifyCode(acceptData.getSystem(), phone, 1);
+            if (!vcode.equalsIgnoreCase(cacheCode)) {
+                return JsonUtil.loadFalseResult("楠岃瘉鐮侀敊璇�");
+            }
+        } else {
+            phone = AliyunOneKeyLoginUtil.getMobile(token, "");
+            if (StringUtil.isNullOrEmpty(phone)) {
+                return JsonUtil.loadFalseResult("鎵嬫満鍙疯幏鍙栧け璐�");
+            }
+        }
+        userService.bindPhone(acceptData.getUid(), phone);
+        return JsonUtil.loadTrueResult("");
+    }
+
+    @ResponseBody
+    @RequestMapping("bindWX")
+    public String bindWX(AcceptData acceptData, String code) {
+        if (acceptData.getUid() == null) {
+            return JsonUtil.loadFalseResult("鐢ㄦ埛鏈櫥褰�");
+        }
+        String wxAppId = systemConfigService.getValueCache(SystemConfigKeyEnum.wxAppId, acceptData.getSystem());
+        String wxAppSecret = systemConfigService.getValueCache(SystemConfigKeyEnum.wxAppSecret, acceptData.getSystem());
+        if (StringUtil.isNullOrEmpty(wxAppId) || StringUtil.isNullOrEmpty(wxAppSecret)) {
+            return JsonUtil.loadFalseResult("鑾峰彇閰嶇疆淇℃伅澶辫触");
+        }
+        WeiXinUser weiXinUser = WXAppLoginUtil.getWeiXinUser(code, wxAppId, wxAppSecret);
+        if (weiXinUser == null) {
+            return JsonUtil.loadFalseResult("鑾峰彇寰俊鎺堟潈淇℃伅澶辫触");
+        }
+        userService.bindWechat(acceptData.getUid(), weiXinUser);
+        return JsonUtil.loadTrueResult("");
+    }
+
+
+    @ResponseBody
+    @RequestMapping("bindQQ")
+    public String bindQQ(AcceptData acceptData, QQUserInfo qqUserInfo) {
+        if (acceptData.getUid() == null) {
+            return JsonUtil.loadFalseResult("鐢ㄦ埛鏈櫥褰�");
+        }
+        if (StringUtil.isNullOrEmpty(qqUserInfo.getOpenid())) {
+            return JsonUtil.loadFalseResult("鑾峰彇QQ鎺堟潈淇℃伅澶辫触");
+        }
+        userService.bindQQ(acceptData.getUid(), qqUserInfo);
+        return JsonUtil.loadTrueResult("");
+    }
+
+
+}
diff --git a/src/main/java/com/everyday/word/controller/user/LoginController.java b/src/main/java/com/everyday/word/controller/user/LoginController.java
deleted file mode 100644
index 14a1980..0000000
--- a/src/main/java/com/everyday/word/controller/user/LoginController.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.everyday.word.controller.user;
-
-import com.everyday.word.utils.AliyunOneKeyLoginUtil;
-import com.everyday.word.vo.AcceptData;
-import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.yeshi.utils.JsonUtil;
-import org.yeshi.utils.StringUtil;
-
-import javax.servlet.http.HttpSession;
-
-/**
- * @author hxh
- * @title: UserController
- * @description:
- * @date 2025/2/7 15:59
- */
-@Controller
-@RequestMapping("user/login")
-public class LoginController {
-
-    /**
-     * @return java.lang.String
-     * @author hxh
-     * @description 鐢佃瘽鍙风爜鐧诲綍
-     * @date 16:08 2025/2/7
-     * @param: acceptData
-     * @param: session
-     **/
-    @ResponseBody
-    @RequestMapping("phone")
-    public String phoneLogin(AcceptData acceptData, HttpSession session) {
-
-
-        return JsonUtil.loadTrueResult("");
-    }
-
-    /**
-     * @return java.lang.String
-     * @author hxh
-     * @description 涓�閿櫥褰�
-     * @date 16:09 2025/2/7
-     * @param: acceptData
-     * @param: token
-     * @param: session
-     **/
-    @ResponseBody
-    @RequestMapping("one_key")
-    public String oneKeyLogin(AcceptData acceptData, String token, HttpSession session) {
-        String phone = AliyunOneKeyLoginUtil.getMobile(token, "");
-        if (StringUtil.isNullOrEmpty(phone)) {
-            return JsonUtil.loadFalseResult("鎵嬫満鍙疯幏鍙栧け璐�");
-        }
-        return JsonUtil.loadTrueResult("");
-    }
-
-    @ResponseBody
-    @RequestMapping("wx")
-    public String wxLogin(AcceptData acceptData, String token, HttpSession session) {
-        return JsonUtil.loadTrueResult("");
-    }
-
-    @ResponseBody
-    @RequestMapping("qq")
-    public String qqLogin(AcceptData acceptData, String token, HttpSession session) {
-        return JsonUtil.loadTrueResult("");
-    }
-
-
-}
diff --git a/src/main/java/com/everyday/word/entity/config/SystemConfig.java b/src/main/java/com/everyday/word/entity/config/SystemConfig.java
index ae0a51c..f0999a1 100644
--- a/src/main/java/com/everyday/word/entity/config/SystemConfig.java
+++ b/src/main/java/com/everyday/word/entity/config/SystemConfig.java
@@ -1,7 +1,9 @@
 package com.everyday.word.entity.config;
 
 import com.everyday.word.entity.SystemEnum;
+import lombok.Builder;
 import lombok.Data;
+import lombok.experimental.Tolerate;
 import org.yeshi.utils.generater.mybatis.Column;
 import org.yeshi.utils.generater.mybatis.Table;
 
@@ -15,20 +17,23 @@
  */
 @Table("table_system_config")
 @Data
+@Builder
 public class SystemConfig {
+
+
     @Column(name = "id")
     private Long id;
 
-    @Column(name = "key")
+    @Column(name = "`key`")
     private String key;
 
-    @Column(name = "value")
+    @Column(name = "`value`")
     private String value;
 
-    @Column(name = "name")
+    @Column(name = "`name`")
     private String name;
 
-    @Column(name = "system")
+    @Column(name = "`system`")
     private SystemEnum system;
 
     @Column(name = "create_time")
@@ -37,21 +42,24 @@
     @Column(name = "update_time")
     private Date updateTime;
 
+    @Tolerate
     public SystemConfig() {
+
     }
 
+    @Tolerate
     public SystemConfig(Long id) {
         super();
         this.id = id;
     }
 
+    @Tolerate
     public SystemConfig(String key, String value, String name, SystemEnum system) {
         this.key = key;
         this.value = value;
         this.name = name;
         this.system = system;
     }
-
 
 
 }
diff --git a/src/main/java/com/everyday/word/entity/config/SystemConfigKeyEnum.java b/src/main/java/com/everyday/word/entity/config/SystemConfigKeyEnum.java
index a933815..e9c0ee6 100644
--- a/src/main/java/com/everyday/word/entity/config/SystemConfigKeyEnum.java
+++ b/src/main/java/com/everyday/word/entity/config/SystemConfigKeyEnum.java
@@ -11,7 +11,15 @@
     wxGZAppId("寰俊鍏紬骞冲彴鐨刟ppId"),
     wxGZAppSecret("寰俊鍏紬骞冲彴鐨刟ppSecret"),
     defaultNickNamePrefix("榛樿鏄电О鍓嶇紑"),
-    defaultPortrait("榛樿澶村儚");
+    defaultPortrait("榛樿澶村儚"),
+    smsAlias("鐭俊绛惧悕"),
+    smsTemplate("鐭俊妯$増"),
+    smsAppId("鐭俊楠岃瘉鐮佺殑AppId"),
+    smsAppKey("鐭俊楠岃瘉鐮佺殑AppKey"),
+    ;
+
+
+
 
     private String name;
 
diff --git a/src/main/java/com/everyday/word/entity/user/User.java b/src/main/java/com/everyday/word/entity/user/User.java
index 253b6a3..4df01f8 100644
--- a/src/main/java/com/everyday/word/entity/user/User.java
+++ b/src/main/java/com/everyday/word/entity/user/User.java
@@ -1,7 +1,9 @@
 package com.everyday.word.entity.user;
 
 import com.everyday.word.entity.SystemEnum;
+import lombok.Builder;
 import lombok.Data;
+import lombok.experimental.Tolerate;
 import org.springframework.data.annotation.Id;
 import org.yeshi.utils.generater.mybatis.Column;
 import org.yeshi.utils.generater.mybatis.Table;
@@ -16,7 +18,12 @@
  */
 @Table("table_user")
 @Data
+@Builder
 public class User {
+    @Tolerate
+    public User(){
+
+    }
     @Id
     @Column(name ="id")
     private Long id;
diff --git a/src/main/java/com/everyday/word/entity/user/UserAuth.java b/src/main/java/com/everyday/word/entity/user/UserAuth.java
index b632b61..0e6c588 100644
--- a/src/main/java/com/everyday/word/entity/user/UserAuth.java
+++ b/src/main/java/com/everyday/word/entity/user/UserAuth.java
@@ -33,13 +33,19 @@
     private Long userId;
     @Column(name ="system")
     private SystemEnum system;
-    // 浣跨敤鏋氫妇绫诲瀷
+    /**
+     * 浣跨敤鏋氫妇绫诲瀷
+     */
     @Column(name ="identity_type")
     private IdentityType identityType;
-    // 鎵嬫満鍙�, openid, unionid
+    /**
+     * 鎵嬫満鍙�, openid, unionid
+     */
     @Column(name ="identifier")
     private String identifier;
-    // 瀵嗙爜鎴杢oken
+    /**
+     * 瀵嗙爜鎴杢oken
+     */
     @Column(name ="credential")
     private String credential;
 
diff --git a/src/main/java/com/everyday/word/factory/user/UserAuthFactory.java b/src/main/java/com/everyday/word/factory/user/UserAuthFactory.java
deleted file mode 100644
index 008749d..0000000
--- a/src/main/java/com/everyday/word/factory/user/UserAuthFactory.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.everyday.word.factory.user;
-
-import com.everyday.word.dto.QQUserInfo;
-import com.everyday.word.entity.user.IdentityType;
-import com.everyday.word.entity.user.UserAuth;
-import org.yeshi.utils.entity.wx.WeiXinUser;
-
-/**
- * @author hxh
- * @title: UserAuthFactory
- * @description: TODO
- * @date 2025/2/11 13:51
- */
-public class UserAuthFactory {
-
-    public static UserAuth create(WeiXinUser user){
-        return UserAuth.builder()
-                .identityType(IdentityType.WECHAT)
-                .identifier(user.getOpenid())
-                .portrait(user.getHeadimgurl())
-                .nickName(user.getNickname())
-                .build();
-    }
-
-    public static UserAuth create(QQUserInfo user){
-        return UserAuth.builder()
-                .identityType(IdentityType.QQ)
-                .identifier(user.getOpenid())
-                .portrait(user.getFigureurl())
-                .nickName(user.getNickname())
-                .build();
-    }
-
-    public static UserAuth create(String phone){
-        return UserAuth.builder()
-                .identityType(IdentityType.PHONE)
-                .identifier(phone)
-                .build();
-    }
-
-}
diff --git a/src/main/java/com/everyday/word/factory/user/UserFactory.java b/src/main/java/com/everyday/word/factory/user/UserFactory.java
new file mode 100644
index 0000000..315a4d3
--- /dev/null
+++ b/src/main/java/com/everyday/word/factory/user/UserFactory.java
@@ -0,0 +1,76 @@
+package com.everyday.word.factory.user;
+
+import com.everyday.word.dto.QQUserInfo;
+import com.everyday.word.entity.SystemEnum;
+import com.everyday.word.entity.user.IdentityType;
+import com.everyday.word.entity.user.User;
+import com.everyday.word.entity.user.UserAuth;
+import com.everyday.word.vo.user.UserInfoVO;
+import org.yeshi.utils.StringUtil;
+import org.yeshi.utils.entity.wx.WeiXinUser;
+
+import java.util.List;
+
+/**
+ * @author hxh
+ * @title: UserAuthFactory
+ * @description: TODO
+ * @date 2025/2/11 13:51
+ */
+public class UserFactory {
+
+    public static UserAuth createAuth(WeiXinUser user, SystemEnum system) {
+        return UserAuth.builder()
+                .identityType(IdentityType.WECHAT)
+                .identifier(user.getOpenid())
+                .portrait(user.getHeadimgurl())
+                .nickName(user.getNickname())
+                .system(system)
+                .build();
+    }
+
+    public static UserAuth createAuth(QQUserInfo user, SystemEnum system) {
+        return UserAuth.builder()
+                .identityType(IdentityType.QQ)
+                .identifier(user.getOpenid())
+                .portrait(user.getFigureurl())
+                .nickName(user.getNickname())
+                .system(system)
+                .build();
+    }
+
+    public static UserAuth createAuth(String phone, SystemEnum system) {
+        return UserAuth.builder()
+                .identityType(IdentityType.PHONE)
+                .identifier(phone)
+                .system(system)
+                .build();
+    }
+
+    public static UserInfoVO createVO(User user, List<UserAuth> authList) {
+        UserInfoVO vo = UserInfoVO.builder()
+                .id(user.getId())
+                .nickName(user.getNickName())
+                .portrait(user.getPortrait())
+                .build();
+        if (authList != null) {
+            for (UserAuth auth : authList) {
+                if (auth.getIdentityType() == IdentityType.PHONE) {
+                    if (auth.getIdentifier().length() > 5) {
+                        String phone = auth.getIdentifier().substring(0, 3);
+                        phone += "******";
+                        phone += auth.getIdentifier().substring(auth.getIdentifier().length() - 2, auth.getIdentifier().length());
+                        vo.setPhone(phone);
+                    } else {
+                        vo.setPhone(auth.getIdentifier());
+                    }
+                } else if (auth.getIdentityType() == IdentityType.WECHAT) {
+                    vo.setWxNickName(auth.getNickName());
+                } else if (auth.getIdentityType() == IdentityType.QQ) {
+                    vo.setQqNickName(auth.getNickName());
+                }
+            }
+        }
+        return vo;
+    }
+}
diff --git a/src/main/java/com/everyday/word/service/RedisManager.java b/src/main/java/com/everyday/word/service/RedisManager.java
new file mode 100644
index 0000000..292bb4a
--- /dev/null
+++ b/src/main/java/com/everyday/word/service/RedisManager.java
@@ -0,0 +1,85 @@
+package com.everyday.word.service;
+
+/**
+ * @author hxh
+ * @title: RedisManager
+ * @description: Redis绠$悊鍣�
+ * @date 2025/2/11 17:13
+ */
+
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.concurrent.TimeUnit;
+
+@Component
+public class RedisManager {
+
+    @Resource
+    private RedisTemplate<String, String> redisTemplate;
+
+
+    /**
+     * 缂撳瓨瀛楃涓�
+     *
+     * @param key
+     * @param value
+     */
+    private void setString(String key, String value) {
+        redisTemplate.opsForValue().set(key, value);
+    }
+
+    /**
+     * 鍒犻櫎鏌愪釜閿��
+     *
+     * @param key
+     */
+    public void removeKey(String key) {
+        redisTemplate.delete(key);
+    }
+
+    /**
+     * 缂撳瓨瀛楃涓�
+     *
+     * @param key
+     * @param value
+     * @param seconds -缂撳瓨鏃堕棿锛坰锛�
+     */
+    private void setString(String key, String value, int seconds) {
+        redisTemplate.opsForValue().set(key, value, seconds, TimeUnit.SECONDS);
+    }
+
+    private String getString(String key) {
+        return redisTemplate.opsForValue().get(key);
+    }
+
+    public void increase(String key) {
+        redisTemplate.opsForValue().increment(key);
+    }
+
+    public void expire(String key, int seconds) {
+        redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
+    }
+
+    public boolean hasKey(String key) {
+        return redisTemplate.hasKey(key);
+    }
+
+    public void cacheCommonString(String key, String value, int seconds) {
+        setString(key, value, seconds);
+    }
+
+    public void cacheCommonString(String key, String value) {
+        setString(key, value);
+    }
+
+    public String getCommonString(String key) {
+        return getString(key);
+    }
+
+    public void removeCommonString(String key) {
+        removeKey(key);
+    }
+
+}
diff --git a/src/main/java/com/everyday/word/service/impl/AdminUserServiceImpl.java b/src/main/java/com/everyday/word/service/impl/AdminUserServiceImpl.java
index 02755e2..5c2dc2e 100644
--- a/src/main/java/com/everyday/word/service/impl/AdminUserServiceImpl.java
+++ b/src/main/java/com/everyday/word/service/impl/AdminUserServiceImpl.java
@@ -2,7 +2,7 @@
 
 import com.everyday.word.dao.AdminUserMapper;
 import com.everyday.word.entity.AdminUser;
-import com.everyday.word.service.AdminUserService;
+import com.everyday.word.service.inter.AdminUserService;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
diff --git a/src/main/java/com/everyday/word/service/impl/EnglishWordsServiceImpl.java b/src/main/java/com/everyday/word/service/impl/EnglishWordsServiceImpl.java
index 8010b87..59a1326 100644
--- a/src/main/java/com/everyday/word/service/impl/EnglishWordsServiceImpl.java
+++ b/src/main/java/com/everyday/word/service/impl/EnglishWordsServiceImpl.java
@@ -3,7 +3,7 @@
 import com.everyday.word.dao.EnglishWordsMapper;
 import com.everyday.word.entity.EnglishWords;
 import com.everyday.word.exception.EnglishWordsException;
-import com.everyday.word.service.EnglishWordsService;
+import com.everyday.word.service.inter.EnglishWordsService;
 import org.springframework.stereotype.Service;
 import org.yeshi.utils.StringUtil;
 
diff --git a/src/main/java/com/everyday/word/service/impl/SMSServiceImpl.java b/src/main/java/com/everyday/word/service/impl/SMSServiceImpl.java
new file mode 100644
index 0000000..9b45fc3
--- /dev/null
+++ b/src/main/java/com/everyday/word/service/impl/SMSServiceImpl.java
@@ -0,0 +1,108 @@
+package com.everyday.word.service.impl;
+
+import com.everyday.word.config.RedisKeyEnum;
+import com.everyday.word.entity.SystemEnum;
+import com.everyday.word.entity.config.SystemConfigKeyEnum;
+import com.everyday.word.exception.SMSException;
+import com.everyday.word.service.RedisManager;
+import com.everyday.word.service.inter.SMSService;
+import com.everyday.word.service.inter.config.SystemConfigService;
+import org.springframework.stereotype.Service;
+import org.yeshi.utils.StringUtil;
+import org.yeshi.utils.sms.TencentSMSUtil;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+/**
+ * @author hxh
+ * @title: SMSServiceImpl
+ * @description: 鐭俊鏈嶅姟
+ * @date 2025/2/11 17:04
+ */
+@Service
+public class SMSServiceImpl implements SMSService {
+
+    @Resource
+    private RedisManager redisManager;
+
+    @Resource
+    private SystemConfigService systemConfigService;
+
+    /**
+     * @return boolean
+     * @author hxh
+     * @description 鏄惁瑙﹀彂楠岃瘉鐮佸彂閫侀鐜�
+     * @date 17:17 2025/2/11
+     * @param: phone
+     * @param: sence
+     **/
+    private boolean isSmsFrequencyLimit(String phone, int sence) {
+        String key = RedisKeyEnum.getRedisKey(RedisKeyEnum.SMS, phone + "-" + sence);
+        String value = redisManager.getCommonString(key);
+        if (StringUtil.isNullOrEmpty(value)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private String getVerifyCode(int length) {
+        String sts = "0123456789";
+        String code = "";
+
+        for (int i = 0; i < length; ++i) {
+            int p = (int) (Math.random() * 10.0D);
+            code = code + sts.charAt(p);
+        }
+
+        return code;
+    }
+
+    private void sendSmsSuccess(String phone, int type) {
+        String key = RedisKeyEnum.getRedisKey(RedisKeyEnum.SMS, phone + "-" + type);
+        redisManager.cacheCommonString(key, "1", 10);
+    }
+
+
+    private void saveSMSVCode(String phone, int type, String code) {
+        String key = RedisKeyEnum.getRedisKey(RedisKeyEnum.SMSVCode, phone + "-" + type);
+        // 淇濆瓨2鍒嗛挓
+        redisManager.cacheCommonString(key, code, 120);
+    }
+
+
+    @Override
+    public String sendVerifyCode(SystemEnum system, String phone, int codeLength, int sence) throws SMSException {
+        boolean limit = isSmsFrequencyLimit(phone, sence);
+        if (limit) {
+            throw new SMSException(1001, "璇疯繃60绉掑啀璇�");
+        }
+        String msgCode = getVerifyCode(codeLength);
+        String smsAlias = systemConfigService.getValueCache(SystemConfigKeyEnum.smsAlias, system);
+        if (StringUtil.isNullOrEmpty(smsAlias)) {
+            throw new SMSException(1001, "鐭俊绛惧悕涓虹┖");
+        }
+        String smsTemplate = systemConfigService.getValueCache(SystemConfigKeyEnum.smsAlias, system);
+        if (StringUtil.isNullOrEmpty(smsTemplate)) {
+            throw new SMSException(1001, "鐭俊妯$増涓虹┖");
+        }
+        // 楠岃瘉鐮佹ā鏉�
+        String msg = smsTemplate.replace("[绛惧悕]", smsAlias).replace("[楠岃瘉鐮乚",
+                msgCode);
+        // 鍙戦�佺煭淇�
+        String appId = systemConfigService.getValueCache(SystemConfigKeyEnum.smsAppId, system);
+        String appKey = systemConfigService.getValueCache(SystemConfigKeyEnum.smsAppKey, system);
+        TencentSMSUtil.sendSingleMsg(Integer.parseInt(appId), appKey, phone, msg);
+        // 缂撳瓨
+        sendSmsSuccess(phone, sence);
+        saveSMSVCode(phone, sence, msgCode);
+        return msgCode;
+    }
+
+    @Override
+    public String getVerifyCode(SystemEnum system, String phone, int sence) {
+        String key = RedisKeyEnum.getRedisKey(RedisKeyEnum.SMSVCode, phone + "-" + sence);
+        return redisManager.getCommonString(key);
+    }
+}
diff --git a/src/main/java/com/everyday/word/service/impl/config/SystemConfigServiceImpl.java b/src/main/java/com/everyday/word/service/impl/config/SystemConfigServiceImpl.java
index 796cf40..fe6a486 100644
--- a/src/main/java/com/everyday/word/service/impl/config/SystemConfigServiceImpl.java
+++ b/src/main/java/com/everyday/word/service/impl/config/SystemConfigServiceImpl.java
@@ -16,7 +16,7 @@
 /**
  * @author hxh
  * @title: SystemConfigServiceImpl
- * @description: TODO
+ * @description: 绯荤粺閰嶇疆鏈嶅姟
  * @date 2025/2/11 15:45
  */
 @Service
@@ -33,8 +33,6 @@
         if (systemConfig == null || systemConfig.getSystem() == null || systemConfig.getKey() == null) {
             throw new ParamsException(1, "鍙傛暟涓嶅畬鏁�");
         }
-
-
         SystemConfig old = getConfig(SystemConfigKeyEnum.valueOf(systemConfig.getKey()), systemConfig.getSystem());
         if (old != null) {
             SystemConfig update = new SystemConfig();
@@ -43,6 +41,9 @@
             update.setUpdateTime(new Date());
             systemConfigMapper.updateByPrimaryKeySelective(update);
         } else {
+            if (systemConfig.getCreateTime() == null) {
+                systemConfig.setCreateTime(new Date());
+            }
             if (systemConfig.getUpdateTime() == null) {
                 systemConfig.setUpdateTime(new Date());
             }
@@ -59,7 +60,7 @@
                 .count(1)
                 .build();
         List<SystemConfig> mList = systemConfigMapper.list(daoQuery);
-        if(mList!=null&&mList.size()>0){
+        if(mList!=null&& !mList.isEmpty()){
             return mList.get(0);
         }
         return null;
diff --git a/src/main/java/com/everyday/word/service/impl/user/UserServiceImpl.java b/src/main/java/com/everyday/word/service/impl/user/UserServiceImpl.java
index a540324..58a5dfe 100644
--- a/src/main/java/com/everyday/word/service/impl/user/UserServiceImpl.java
+++ b/src/main/java/com/everyday/word/service/impl/user/UserServiceImpl.java
@@ -1,5 +1,6 @@
 package com.everyday.word.service.impl.user;
 
+import com.everyday.word.config.CosFilePathEnum;
 import com.everyday.word.dao.user.UserAuthMapper;
 import com.everyday.word.dao.user.UserMapper;
 import com.everyday.word.dto.QQUserInfo;
@@ -8,14 +9,17 @@
 import com.everyday.word.entity.user.User;
 import com.everyday.word.entity.user.UserAuth;
 import com.everyday.word.exception.user.UserException;
-import com.everyday.word.factory.user.UserAuthFactory;
+import com.everyday.word.factory.user.UserFactory;
 import com.everyday.word.service.inter.user.UserService;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 import org.yeshi.utils.StringUtil;
 import org.yeshi.utils.entity.wx.WeiXinUser;
+import org.yeshi.utils.tencentcloud.COSManager;
 
 import javax.annotation.Resource;
+import java.io.IOException;
 import java.util.Date;
 import java.util.List;
 
@@ -31,6 +35,8 @@
     private UserMapper userMapper;
     @Resource
     private UserAuthMapper userAuthMapper;
+    @Resource
+    private COSManager cosManager;
 
     @Override
     public User loginByWX(WeiXinUser user, SystemEnum system) {
@@ -73,6 +79,7 @@
         user.setNickName(authInfo.getNickName());
         user.setPortrait(authInfo.getPortrait());
         user.setCreateTime(new Date());
+        user.setSystem(authInfo.getSystem());
         userMapper.insertSelective(user);
         authInfo.setUserId(user.getId());
         authInfo.setCreateTime(new Date());
@@ -87,7 +94,7 @@
         daoQuery.system = system;
         daoQuery.count = 1;
         List<UserAuth> authList = userAuthMapper.list(daoQuery);
-        if (authList.size() < 1) {
+        if (authList.isEmpty()) {
             return null;
         }
         return authList.get(0);
@@ -100,7 +107,7 @@
         daoQuery.userId = uid;
         daoQuery.count = 1;
         List<UserAuth> authList = userAuthMapper.list(daoQuery);
-        if (authList.size() < 1) {
+        if (authList.isEmpty()) {
             return null;
         }
         return authList.get(0);
@@ -125,7 +132,7 @@
         if (user == null) {
             throw new UserException("鐢ㄦ埛涓嶅瓨鍦�");
         }
-        UserAuth userAuth = UserAuthFactory.create(weiXinUser);
+        UserAuth userAuth = UserFactory.createAuth(weiXinUser, user.getSystem());
         if (selectUserAuth(userAuth.getIdentityType(), userAuth.getIdentifier(), user.getSystem()) != null) {
             throw new UserException("寰俊宸茶缁戝畾");
         }
@@ -138,7 +145,7 @@
         if (user == null) {
             throw new UserException("鐢ㄦ埛涓嶅瓨鍦�");
         }
-        UserAuth userAuth = UserAuthFactory.create(qqUserInfo);
+        UserAuth userAuth = UserFactory.createAuth(qqUserInfo, user.getSystem());
         if (selectUserAuth(userAuth.getIdentityType(), userAuth.getIdentifier(), user.getSystem()) != null) {
             throw new UserException("QQ宸茶缁戝畾");
         }
@@ -151,13 +158,24 @@
         if (user == null) {
             throw new UserException("鐢ㄦ埛涓嶅瓨鍦�");
         }
-        UserAuth userAuth = UserAuthFactory.create(phone);
+        UserAuth userAuth = UserFactory.createAuth(phone, user.getSystem());
         if (selectUserAuth(userAuth.getIdentityType(), userAuth.getIdentifier(), user.getSystem()) != null) {
             throw new UserException("鎵嬫満鍙峰凡琚粦瀹�");
         }
         bind(userId, userAuth);
     }
 
+    @Override
+    public void uploadPortrait(MultipartFile file, Long uid) throws UserException, IOException {
+        String fileUrl = cosManager.uploadFile(file.getInputStream(), CosFilePathEnum.userPortrait.getWholePath(uid + ".jpg")).getUrl();
+        User user = User.builder()
+                .id(uid)
+                .portrait(fileUrl)
+                .updateTime(new Date())
+                .build();
+        userMapper.updateByPrimaryKeySelective(user);
+    }
+
     private void bind(Long userId, UserAuth userAuth) {
         // 鏌ヨ鏄惁涓烘洿鎹㈢粦瀹�
         UserAuth oldAuth = selectUserAuth(userId, userAuth.getIdentityType());
diff --git a/src/main/java/com/everyday/word/service/AdminUserService.java b/src/main/java/com/everyday/word/service/inter/AdminUserService.java
similarity index 87%
rename from src/main/java/com/everyday/word/service/AdminUserService.java
rename to src/main/java/com/everyday/word/service/inter/AdminUserService.java
index c25bbb6..a7f6697 100644
--- a/src/main/java/com/everyday/word/service/AdminUserService.java
+++ b/src/main/java/com/everyday/word/service/inter/AdminUserService.java
@@ -1,4 +1,4 @@
-package com.everyday.word.service;
+package com.everyday.word.service.inter;
 
 
 import com.everyday.word.entity.AdminUser;
diff --git a/src/main/java/com/everyday/word/service/EnglishWordsService.java b/src/main/java/com/everyday/word/service/inter/EnglishWordsService.java
similarity index 95%
rename from src/main/java/com/everyday/word/service/EnglishWordsService.java
rename to src/main/java/com/everyday/word/service/inter/EnglishWordsService.java
index a0bad00..3a90009 100644
--- a/src/main/java/com/everyday/word/service/EnglishWordsService.java
+++ b/src/main/java/com/everyday/word/service/inter/EnglishWordsService.java
@@ -1,4 +1,4 @@
-package com.everyday.word.service;
+package com.everyday.word.service.inter;
 
 import com.everyday.word.dao.EnglishWordsMapper;
 import com.everyday.word.entity.EnglishWords;
diff --git a/src/main/java/com/everyday/word/service/inter/SMSService.java b/src/main/java/com/everyday/word/service/inter/SMSService.java
new file mode 100644
index 0000000..62ebaca
--- /dev/null
+++ b/src/main/java/com/everyday/word/service/inter/SMSService.java
@@ -0,0 +1,39 @@
+package com.everyday.word.service.inter;
+
+import com.everyday.word.entity.SystemEnum;
+import com.everyday.word.exception.SMSException;
+
+import java.math.BigDecimal;
+
+/**
+ * 鐭俊鏈嶅姟
+ * 
+ * @author Administrator
+ *
+ */
+public interface SMSService {
+
+	/**
+	 * @author hxh 
+	 * @description 鍙戦�侀獙璇佺煭淇�
+	 * @date 17:06 2025/2/11
+	 * @param: system
+	 * @param: phone
+	 * @param: codeLength
+	 * @param: sence
+	 * @return java.lang.String
+	 **/
+	public String sendVerifyCode(SystemEnum system, String phone, int codeLength, int sence) throws SMSException;
+
+	/**
+	 * @author hxh 
+	 * @description 鑾峰彇楠岃瘉鐮�
+	 * @date 13:07 2025/2/13
+	 * @param: system
+	 * @param: phone
+	 * @param: sence
+	 * @return java.lang.String
+	 **/
+	public String getVerifyCode(SystemEnum system, String phone, int sence);
+
+}
diff --git a/src/main/java/com/everyday/word/service/inter/user/UserService.java b/src/main/java/com/everyday/word/service/inter/user/UserService.java
index cc3abff..5e79ad6 100644
--- a/src/main/java/com/everyday/word/service/inter/user/UserService.java
+++ b/src/main/java/com/everyday/word/service/inter/user/UserService.java
@@ -6,8 +6,10 @@
 import com.everyday.word.entity.user.User;
 import com.everyday.word.entity.user.UserAuth;
 import com.everyday.word.exception.user.UserException;
+import org.springframework.web.multipart.MultipartFile;
 import org.yeshi.utils.entity.wx.WeiXinUser;
 
+import java.io.IOException;
 import java.util.List;
 
 /**
@@ -129,4 +131,15 @@
     public void bindPhone(Long userId, String phone) throws UserException;
 
 
+    /**
+     * @author hxh 
+     * @description 涓婁紶澶村儚
+     * @date 11:15 2025/2/13
+     * @param: file
+     * @param: uid
+     * @return void
+     **/
+    public void uploadPortrait(MultipartFile file, Long uid) throws UserException, IOException;
+
+
 }
diff --git a/src/main/java/com/everyday/word/utils/AliyunOneKeyLoginUtil.java b/src/main/java/com/everyday/word/utils/AliyunOneKeyLoginUtil.java
index af2c4ec..7a4b725 100644
--- a/src/main/java/com/everyday/word/utils/AliyunOneKeyLoginUtil.java
+++ b/src/main/java/com/everyday/word/utils/AliyunOneKeyLoginUtil.java
@@ -28,8 +28,7 @@
             GetMobileResponse response = client.getAcsResponse(request);
             System.out.println(new Gson().toJson(response));
             if (response.getGetMobileResultDTO() != null) {
-                String mobile = response.getGetMobileResultDTO().getMobile();
-                return mobile;
+                return response.getGetMobileResultDTO().getMobile();
             }
             System.out.println(new Gson().toJson(response));
         } catch (ServerException e) {
diff --git a/src/main/java/com/everyday/word/vo/AcceptData.java b/src/main/java/com/everyday/word/vo/AcceptData.java
index d6f03f6..3874a64 100644
--- a/src/main/java/com/everyday/word/vo/AcceptData.java
+++ b/src/main/java/com/everyday/word/vo/AcceptData.java
@@ -6,7 +6,7 @@
 /**
  * @author hxh
  * @title: AcceptData
- * @description: TODO
+ * @description: 鎺ュ彛鎺ュ彈鍩虹鏁版嵁
  * @date 2025/2/7 16:03
  */
 @Data
@@ -15,10 +15,10 @@
     private String packages;
     private long timestamp;
     private String sign;
-    private int versionCode;
     private String platform;
     private String channel;
     private int version;
     private String osVersion;
     private SystemEnum system;
+    private Long uid;
 }
diff --git a/src/main/java/com/everyday/word/vo/user/UserInfoVO.java b/src/main/java/com/everyday/word/vo/user/UserInfoVO.java
new file mode 100644
index 0000000..a529ea4
--- /dev/null
+++ b/src/main/java/com/everyday/word/vo/user/UserInfoVO.java
@@ -0,0 +1,22 @@
+package com.everyday.word.vo.user;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * @author hxh
+ * @title: UserInfoVO
+ * @description: 鐢ㄦ埛淇℃伅杈撳嚭
+ * @date 2025/2/13 13:53
+ */
+@Data
+@Builder
+public class UserInfoVO {
+    private Long id;
+    private String nickName;
+    private String portrait;
+    private String phone;
+    private String wxNickName;
+    private String qqNickName;
+
+}
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 367711e..664931c 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -45,8 +45,40 @@
     type: ehcache
     ehcache:
       config: classpath:ehcache.xml
+  redis:
+    host: 192.168.3.252
+    port: 6379
+    password: 123456
+    database: 0
+    timeout: 5000
+    lettuce:
+      pool:
+        max-active: 200 #杩炴帴姹犳渶澶ц繛鎺ユ暟
+        max-wait: 5000 #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂�
+        max-idle: 50 #杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+        time-between-eviction-runs: 60000 #姣忛殧澶氶暱鏃堕棿杩愯涓�娆$┖闂茶繛鎺ュ洖鏀跺櫒锛堢嫭绔嬬嚎绋嬶級
+        min-idle: 5 #杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+      shutdown-timeout: 5000 #杩炴帴鍏抽棴瓒呮椂鏃堕棿
+    jedis:
+      pool:
+        max-total: 1024
+        max-wait: 10000ms
+        max-idle: 200
+        test_on_borrow: true
+        min-idle: 8
+
 mybatis:
   mapper-locations: classpath:mapper/*.xml
   type-aliases-package: com.everyday.word.entity
   configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
\ No newline at end of file
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+# 鑵捐浜慍OS璁剧疆
+qcloud:
+  cos:
+     appid: 1255749512
+     secret_id: AKIDTlpgJhLjOozvd6QI2XnpfGbgV4NQJk25
+     secret_key: xhCSUHo55oHUQ6XicFcmfIgspX0EEzWo
+     bucket_name: words
+     region: ap-guangzhou
+     access_host:
+
diff --git a/src/main/resources/application-pro.yml b/src/main/resources/application-pro.yml
index 3f4fd77..3ee70fb 100644
--- a/src/main/resources/application-pro.yml
+++ b/src/main/resources/application-pro.yml
@@ -16,9 +16,9 @@
     #    username: root
     #    password: Yeshi2016@
     # XCP
-    url: jdbc:mysql://rm-f8z0j143g151fp995.mysql.rds.aliyuncs.com:3306/taoke_autopay?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
+    url: jdbc:mysql://172.16.16.17:3306/everyday_words?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
     username: root
-    password: xcp123123@
+    password: Yeshi2016@
 
     driver-class-name: com.mysql.jdbc.Driver
     type: com.alibaba.druid.pool.DruidDataSource
@@ -43,8 +43,39 @@
     type: ehcache
     ehcache:
       config: classpath:ehcache.xml
+  redis:
+    host: localhost
+    port: 6379
+    password: 123213
+    database: 0
+    timeout: 5000
+    lettuce:
+      pool:
+        max-active: 200 #杩炴帴姹犳渶澶ц繛鎺ユ暟
+        max-wait: 5000 #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂�
+        max-idle: 50 #杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+        time-between-eviction-runs: 60000 #姣忛殧澶氶暱鏃堕棿杩愯涓�娆$┖闂茶繛鎺ュ洖鏀跺櫒锛堢嫭绔嬬嚎绋嬶級
+        min-idle: 5 #杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+      shutdown-timeout: 5000 #杩炴帴鍏抽棴瓒呮椂鏃堕棿
+    jedis:
+        pool:
+          max-total: 1024
+          max-wait: 10000ms
+          max-idle: 200
+          test_on_borrow: true
+          min-idle: 8
 mybatis:
   mapper-locations: classpath:mapper/*.xml
-  type-aliases-package: com.taoke.autopay.entity
+  type-aliases-package: com.everyday.word.entity
   configuration:
-    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
\ No newline at end of file
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+
+# 鑵捐浜慍OS璁剧疆
+qcloud:
+  cos:
+    appid: 1255749512
+    secret_id: AKIDTlpgJhLjOozvd6QI2XnpfGbgV4NQJk25
+    secret_key: xhCSUHo55oHUQ6XicFcmfIgspX0EEzWo
+    bucket_name: words
+    region: ap-guangzhou
+    access_host:
\ No newline at end of file
diff --git a/src/main/resources/base.sql b/src/main/resources/base.sql
new file mode 100644
index 0000000..0073b85
--- /dev/null
+++ b/src/main/resources/base.sql
@@ -0,0 +1,46 @@
+create table table_user
+(
+    `id`          bigint not null primary key AUTO_INCREMENT,
+    `nick_name`   varchar(128) DEFAULT NULL,
+    `portrait`    varchar(256) DEFAULT NULL,
+    `system`      varchar(64)  DEFAULT NULL,
+    `create_time` datetime     DEFAULT NULL,
+    `update_time` datetime     DEFAULT NULL
+);
+
+create table table_user_auth
+(
+    `id`            bigint not null primary key AUTO_INCREMENT,
+    `user_id`       bigint       DEFAULT NULL,
+    `system`        varchar(64)  DEFAULT NULL,
+    `identity_type` varchar(128) DEFAULT NULL,
+    `identifier`    varchar(128) DEFAULT NULL,
+    `credential`    varchar(128) DEFAULT NULL,
+    `nick_name`     varchar(128) DEFAULT NULL,
+    `portrait`      varchar(256) DEFAULT NULL,
+    `create_time`   datetime     DEFAULT NULL,
+    `update_time`   datetime     DEFAULT NULL
+);
+
+create table table_admin
+(
+    `id`          bigint not null primary key AUTO_INCREMENT,
+    `account`     varchar(128) DEFAULT NULL,
+    `pwd`         varchar(128) DEFAULT NULL,
+    `name`        varchar(128) DEFAULT NULL,
+    `rule`        int          DEFAULT NULL,
+    `create_time` datetime     DEFAULT NULL,
+    `update_time` datetime     DEFAULT NULL
+);
+
+create table table_system_config
+(
+    `id`          bigint not null primary key AUTO_INCREMENT,
+    `key`         varchar(128) DEFAULT NULL,
+    `value`       varchar(1024) DEFAULT NULL,
+    `name`        varchar(128) DEFAULT NULL,
+    `system`      varchar(128) DEFAULT NULL,
+    `create_time` datetime     DEFAULT NULL,
+    `update_time` datetime     DEFAULT NULL
+);
+
diff --git a/src/main/resources/mapper/SystemConfigMapper.xml b/src/main/resources/mapper/SystemConfigMapper.xml
index 6f4eab9..887a0d5 100644
--- a/src/main/resources/mapper/SystemConfigMapper.xml
+++ b/src/main/resources/mapper/SystemConfigMapper.xml
@@ -2,74 +2,97 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 
 <mapper namespace="com.everyday.word.dao.config.SystemConfigMapper">
-  <resultMap id="BaseResultMap" type="com.everyday.word.entity.config.SystemConfig">
-    <id column="id" property="id" jdbcType="BIGINT"/>
-    <result column="key" property="key" jdbcType="VARCHAR"/>
-    <result column="value" property="value" jdbcType="VARCHAR"/>
-    <result column="name" property="name" jdbcType="VARCHAR"/>
-    <result column="system" property="system" jdbcType="VARCHAR"/>
-    <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
-    <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
-  </resultMap>
-  <sql id="Base_Column_List">id,key,value,name,system,create_time,update_time</sql>
-  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">select
-    <include refid="Base_Column_List"/> from table_system_config where id = #{id,jdbcType=BIGINT}
-  </select>
-  <select id="selectByPrimaryKeyForUpdate" resultMap="BaseResultMap" parameterType="java.lang.Long">select
-    <include refid="Base_Column_List"/> from table_system_config where id = #{id,jdbcType=BIGINT} for update
-  </select>
-  <sql id="listWhereSQL">
-    <if test="query.id!=null">AND id = #{query.id}</if>
-    <if test="query.key!=null">AND key = #{query.key}</if>
-    <if test="query.value!=null">AND value = #{query.value}</if>
-    <if test="query.name!=null">AND name = #{query.name}</if>
-    <if test="query.system!=null">AND system = #{query.system}</if>
-    <if test="query.minCreateTime!=null">AND create_time >= #{query.minCreateTime}</if>
-    <if test="query.maxCreateTime!=null">AND #{query.maxCreateTime} > create_time</if>
-    <if test="query.minUpdateTime!=null">AND update_time >= #{query.minUpdateTime}</if>
-    <if test="query.maxUpdateTime!=null">AND #{query.maxUpdateTime} > update_time</if>
-  </sql>
-  <select id="list" resultMap="BaseResultMap">select
-    <include refid="Base_Column_List"/> from table_system_config where 1=1
-    <include refid="listWhereSQL"/>
-    <if test="query.sortList!=null">
-      <foreach collection="query.sortList" item="item" open=" order by " separator=",">#{item}</foreach>
-    </if>limit #{query.start},#{query.count}
-  </select>
-  <select id="count" resultType="java.lang.Long">select count(*) from table_system_config where 1=1
-    <include refid="listWhereSQL"/>
-  </select>
-  <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete from table_system_config where id = #{id,jdbcType=BIGINT}</delete>
-  <insert id="insert" parameterType="com.everyday.word.entity.config.SystemConfig" useGeneratedKeys="true" keyProperty="id">insert into table_system_config (id,key,value,name,system,create_time,update_time) values (#{id,jdbcType=BIGINT},#{key,jdbcType=VARCHAR},#{value,jdbcType=VARCHAR},#{name,jdbcType=VARCHAR},#{system,jdbcType=VARCHAR},#{createTime,jdbcType=TIMESTAMP},#{updateTime,jdbcType=TIMESTAMP})</insert>
-  <insert id="insertSelective" parameterType="com.everyday.word.entity.config.SystemConfig" useGeneratedKeys="true" keyProperty="id">insert into table_system_config
-    <trim prefix="(" suffix=")" suffixOverrides=",">
-      <if test="id != null">id,</if>
-      <if test="key != null">key,</if>
-      <if test="value != null">value,</if>
-      <if test="name != null">name,</if>
-      <if test="system != null">system,</if>
-      <if test="createTime != null">create_time,</if>
-      <if test="updateTime != null">update_time,</if>
-    </trim>values
-    <trim prefix="(" suffix=")" suffixOverrides=",">
-      <if test="id != null">#{id,jdbcType=BIGINT},</if>
-      <if test="key != null">#{key,jdbcType=VARCHAR},</if>
-      <if test="value != null">#{value,jdbcType=VARCHAR},</if>
-      <if test="name != null">#{name,jdbcType=VARCHAR},</if>
-      <if test="system != null">#{system,jdbcType=VARCHAR},</if>
-      <if test="createTime != null">#{createTime,jdbcType=TIMESTAMP},</if>
-      <if test="updateTime != null">#{updateTime,jdbcType=TIMESTAMP},</if>
-    </trim>
-  </insert>
-  <update id="updateByPrimaryKey" parameterType="com.everyday.word.entity.config.SystemConfig">update table_system_config set key = #{key,jdbcType=VARCHAR},value = #{value,jdbcType=VARCHAR},name = #{name,jdbcType=VARCHAR},system = #{system,jdbcType=VARCHAR},create_time = #{createTime,jdbcType=TIMESTAMP},update_time = #{updateTime,jdbcType=TIMESTAMP} where id = #{id,jdbcType=BIGINT}</update>
-  <update id="updateByPrimaryKeySelective" parameterType="com.everyday.word.entity.config.SystemConfig">update table_system_config
-    <set>
-      <if test="key != null">key=#{key,jdbcType=VARCHAR},</if>
-      <if test="value != null">value=#{value,jdbcType=VARCHAR},</if>
-      <if test="name != null">name=#{name,jdbcType=VARCHAR},</if>
-      <if test="system != null">system=#{system,jdbcType=VARCHAR},</if>
-      <if test="createTime != null">create_time=#{createTime,jdbcType=TIMESTAMP},</if>
-      <if test="updateTime != null">update_time=#{updateTime,jdbcType=TIMESTAMP},</if>
-    </set> where id = #{id,jdbcType=BIGINT}
-  </update>
+    <resultMap id="BaseResultMap" type="com.everyday.word.entity.config.SystemConfig">
+        <id column="id" property="id" jdbcType="BIGINT"/>
+        <result column="key" property="key" jdbcType="VARCHAR"/>
+        <result column="value" property="value" jdbcType="VARCHAR"/>
+        <result column="name" property="name" jdbcType="VARCHAR"/>
+        <result column="system" property="system" jdbcType="VARCHAR"/>
+        <result column="create_time" property="createTime" jdbcType="TIMESTAMP"/>
+        <result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
+    </resultMap>
+    <sql id="Base_Column_List">id
+    ,`key`,`value`,`name`,`system`,create_time,update_time</sql>
+    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long">select
+        <include refid="Base_Column_List"/>
+        from table_system_config where id = #{id,jdbcType=BIGINT}
+    </select>
+    <select id="selectByPrimaryKeyForUpdate" resultMap="BaseResultMap" parameterType="java.lang.Long">select
+        <include refid="Base_Column_List"/>
+        from table_system_config where id = #{id,jdbcType=BIGINT} for update
+    </select>
+    <sql id="listWhereSQL">
+        <if test="query.id!=null">AND id = #{query.id}</if>
+        <if test="query.key!=null">AND `key` = #{query.key}</if>
+        <if test="query.value!=null">AND `value` = #{query.value}</if>
+        <if test="query.name!=null">AND `name` = #{query.name}</if>
+        <if test="query.system!=null">AND `system` = #{query.system}</if>
+        <if test="query.minCreateTime!=null">AND create_time >= #{query.minCreateTime}</if>
+        <if test="query.maxCreateTime!=null">AND #{query.maxCreateTime} > create_time</if>
+        <if test="query.minUpdateTime!=null">AND update_time >= #{query.minUpdateTime}</if>
+        <if test="query.maxUpdateTime!=null">AND #{query.maxUpdateTime} > update_time</if>
+    </sql>
+    <select id="list" resultMap="BaseResultMap">select
+        <include refid="Base_Column_List"/>
+        from table_system_config where 1=1
+        <include refid="listWhereSQL"/>
+        <if test="query.sortList!=null">
+            <foreach collection="query.sortList" item="item" open=" order by " separator=",">#{item}</foreach>
+        </if>
+        limit #{query.start},#{query.count}
+    </select>
+    <select id="count" resultType="java.lang.Long">select count(*) from table_system_config where 1=1
+        <include refid="listWhereSQL"/>
+    </select>
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">delete
+                                                                   from table_system_config
+                                                                   where id = #{id,jdbcType=BIGINT}</delete>
+    <insert id="insert" parameterType="com.everyday.word.entity.config.SystemConfig" useGeneratedKeys="true"
+            keyProperty="id">insert into table_system_config (id, `key`, `value`, `name`, `system`, create_time, update_time)
+                             values (#{id,jdbcType=BIGINT}, #{key,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR},
+                                     #{name,jdbcType=VARCHAR}, #{system,jdbcType=VARCHAR},
+                                     #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP})</insert>
+    <insert id="insertSelective" parameterType="com.everyday.word.entity.config.SystemConfig" useGeneratedKeys="true"
+            keyProperty="id">insert into table_system_config
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="key != null">`key`,</if>
+            <if test="value != null">`value`,</if>
+            <if test="name != null">`name`,</if>
+            <if test="system != null">`system`,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+        </trim>
+        values
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id,jdbcType=BIGINT},</if>
+            <if test="key != null">#{key,jdbcType=VARCHAR},</if>
+            <if test="value != null">#{value,jdbcType=VARCHAR},</if>
+            <if test="name != null">#{name,jdbcType=VARCHAR},</if>
+            <if test="system != null">#{system,jdbcType=VARCHAR},</if>
+            <if test="createTime != null">#{createTime,jdbcType=TIMESTAMP},</if>
+            <if test="updateTime != null">#{updateTime,jdbcType=TIMESTAMP},</if>
+        </trim>
+    </insert>
+    <update id="updateByPrimaryKey"
+            parameterType="com.everyday.word.entity.config.SystemConfig">update table_system_config
+                                                                         set `key`         = #{key,jdbcType=VARCHAR},
+                                                                             `value`       = #{value,jdbcType=VARCHAR},
+                                                                             `name`        = #{name,jdbcType=VARCHAR},
+                                                                             `system`      = #{system,jdbcType=VARCHAR},
+                                                                             create_time = #{createTime,jdbcType=TIMESTAMP},
+                                                                             update_time = #{updateTime,jdbcType=TIMESTAMP}
+                                                                         where id = #{id,jdbcType=BIGINT}</update>
+    <update id="updateByPrimaryKeySelective" parameterType="com.everyday.word.entity.config.SystemConfig">update
+        table_system_config
+        <set>
+            <if test="key != null">`key`=#{key,jdbcType=VARCHAR},</if>
+            <if test="value != null">`value`=#{value,jdbcType=VARCHAR},</if>
+            <if test="name != null">`name`=#{name,jdbcType=VARCHAR},</if>
+            <if test="system != null">`system`=#{system,jdbcType=VARCHAR},</if>
+            <if test="createTime != null">create_time=#{createTime,jdbcType=TIMESTAMP},</if>
+            <if test="updateTime != null">update_time=#{updateTime,jdbcType=TIMESTAMP},</if>
+        </set>
+        where id = #{id,jdbcType=BIGINT}
+    </update>
 </mapper>
diff --git a/src/main/resources/mapper/UserAuthMapper.xml b/src/main/resources/mapper/UserAuthMapper.xml
index 483f6de..549b64c 100644
--- a/src/main/resources/mapper/UserAuthMapper.xml
+++ b/src/main/resources/mapper/UserAuthMapper.xml
@@ -1,7 +1,7 @@
 <?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.everyday.word.entity.dao.UserAuthMapper">
+<mapper namespace="com.everyday.word.dao.user.UserAuthMapper">
   <resultMap id="BaseResultMap" type="com.everyday.word.entity.user.UserAuth">
     <id column="id" property="id" jdbcType="BIGINT"/>
     <result column="user_id" property="userId" jdbcType="BIGINT"/>
diff --git a/src/main/resources/mapper/UserMapper.xml b/src/main/resources/mapper/UserMapper.xml
index 7033377..d6c37ed 100644
--- a/src/main/resources/mapper/UserMapper.xml
+++ b/src/main/resources/mapper/UserMapper.xml
@@ -1,7 +1,7 @@
 <?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.everyday.word.entity.dao.UserMapper">
+<mapper namespace="com.everyday.word.dao.user.UserMapper">
   <resultMap id="BaseResultMap" type="com.everyday.word.entity.user.User">
     <id column="id" property="id" jdbcType="BIGINT"/>
     <result column="nick_name" property="nickName" jdbcType="VARCHAR"/>
diff --git a/src/test/java/com/everyday/word/ConfigTest.java b/src/test/java/com/everyday/word/ConfigTest.java
new file mode 100644
index 0000000..e3880f9
--- /dev/null
+++ b/src/test/java/com/everyday/word/ConfigTest.java
@@ -0,0 +1,47 @@
+package com.everyday.word;
+
+import com.everyday.word.entity.SystemEnum;
+import com.everyday.word.entity.config.SystemConfig;
+import com.everyday.word.entity.config.SystemConfigKeyEnum;
+import com.everyday.word.exception.ParamsException;
+import com.everyday.word.service.inter.config.SystemConfigService;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.yeshi.utils.tencentcloud.COSManager;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * @author hxh
+ * @title: RedisTest
+ * @description: COS娴嬭瘯
+ * @date 2025/2/13 10:11
+ */
+@SpringBootTest
+public class ConfigTest {
+
+    @Resource
+    private SystemConfigService systemConfigService;
+
+    @Test
+    public void test() throws ParamsException {
+        SystemConfig config = SystemConfig.builder()
+                .system(SystemEnum.everyday_words)
+                .key(SystemConfigKeyEnum.wxAppId.name())
+                .value("wx82f0e543363453c6")
+                .name(SystemConfigKeyEnum.wxAppId.getName())
+                .build();
+        systemConfigService.save(config);
+
+        config = SystemConfig.builder()
+                .system(SystemEnum.everyday_words)
+                .key(SystemConfigKeyEnum.wxAppSecret.name())
+                .value("8a6164408b9190d0b824f799ce0975b4")
+                .name(SystemConfigKeyEnum.wxAppSecret.getName())
+                .build();
+        systemConfigService.save(config);
+    }
+
+
+}
diff --git a/src/test/java/com/everyday/word/CosTest.java b/src/test/java/com/everyday/word/CosTest.java
new file mode 100644
index 0000000..e332b91
--- /dev/null
+++ b/src/test/java/com/everyday/word/CosTest.java
@@ -0,0 +1,28 @@
+package com.everyday.word;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.yeshi.utils.tencentcloud.COSManager;
+
+import javax.annotation.Resource;
+import java.io.File;
+
+/**
+ * @author hxh
+ * @title: RedisTest
+ * @description: COS娴嬭瘯
+ * @date 2025/2/13 10:11
+ */
+@SpringBootTest
+public class CosTest {
+
+    @Resource
+    private COSManager cosManager;
+
+    @Test
+    public void test() {
+        cosManager.uploadFile(new File("D:\\椤圭洰\\鍗曡瘝\\婧愪唬鐮乗\server\\src\\main\\resources\\static\\index.html"),"user/test.html");
+    }
+
+
+}
diff --git a/src/test/java/com/everyday/word/RedisTest.java b/src/test/java/com/everyday/word/RedisTest.java
new file mode 100644
index 0000000..0efb6c5
--- /dev/null
+++ b/src/test/java/com/everyday/word/RedisTest.java
@@ -0,0 +1,28 @@
+package com.everyday.word;
+
+import com.everyday.word.service.RedisManager;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+import javax.annotation.Resource;
+
+/**
+ * @author hxh
+ * @title: RedisTest
+ * @description: TODO
+ * @date 2025/2/13 10:11
+ */
+@SpringBootTest
+public class RedisTest {
+
+    @Resource
+    private RedisManager redisManager;
+
+    @Test
+    public void test() {
+//        redisManager.cacheCommonString("test","123123",10);
+        System.out.println(redisManager.getCommonString("test"));
+    }
+
+
+}
diff --git a/src/test/java/com/everyday/word/WordsTest.java b/src/test/java/com/everyday/word/WordsTest.java
index c19a104..2052bd8 100644
--- a/src/test/java/com/everyday/word/WordsTest.java
+++ b/src/test/java/com/everyday/word/WordsTest.java
@@ -2,7 +2,7 @@
 
 import com.everyday.word.dao.EnglishWordsMapper;
 import com.everyday.word.entity.EnglishWords;
-import com.everyday.word.service.EnglishWordsService;
+import com.everyday.word.service.inter.EnglishWordsService;
 import com.everyday.word.utils.YouDaoWebApi;
 import com.everyday.word.utils.YouDaoWebUtil;
 import net.sf.json.JSONArray;

--
Gitblit v1.8.0