huangxiao 2 týždňov pred
rodič
commit
7229fac415

+ 3 - 2
bin/start.bat

@@ -1,5 +1,6 @@
 @echo off
 
-start javaw -jar -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -Duser.language=zh -Duser.region=CN ./ms-mcms.jar
+::start D:/Professional/jdk/graalvm-jdk-17.0.9+11.1/bin/java.exe -jar -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -Duser.language=zh -Duser.region=CN ./ms-mcms.jar
+D:/Professional/jdk/graalvm-jdk-17.0.9+11.1/bin/java.exe -jar -Dfile.encoding=utf-8 -Duser.timezone=GMT+08 -Duser.language=zh -Duser.region=CN ./ms-mcms.jar
 :: > boot.log 2>&1
-exit
+::exit

BIN
city.sql.zip


+ 19 - 7
pom.xml

@@ -16,27 +16,39 @@
     <properties>
         <java.version>17</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <ms.version>3.0.3</ms.version>
     </properties>
     <dependencies>
         <dependency>
             <groupId>net.mingsoft</groupId>
             <artifactId>ms-base</artifactId>
-            <version>3.0.3</version>
+            <version>${ms.version}</version>
         </dependency>
         <dependency>
             <groupId>net.mingsoft</groupId>
             <artifactId>ms-basic</artifactId>
-            <version>3.0.3</version>
+            <version>${ms.version}</version>
         </dependency>
         <dependency>
             <groupId>net.mingsoft</groupId>
             <artifactId>ms-mdiy</artifactId>
-            <version>3.0.3</version>
+            <version>${ms.version}</version>
         </dependency>
         <dependency>
             <groupId>net.mingsoft</groupId>
             <artifactId>store-client</artifactId>
-            <version>3.0.3</version>
+            <version>${ms.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>net.mingsoft</groupId>
+            <artifactId>ms-msend</artifactId>
+            <version>3.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>net.mingsoft</groupId>
+            <artifactId>ms-mpeople</artifactId>
+            <version>${ms.version}</version>
         </dependency>
         <dependency>
             <groupId>com.github.oshi</groupId>
@@ -57,13 +69,13 @@
                 <excludes>
                     <!-- 可以避免将静态资源打入jar包中,方便生产实时修改静态资源文件-->
                     <!-- 打包生产建议并手动将static、html、upload、template复制到生产 -->
-                    <!--<exclude>static/</exclude>
+                    <!--<exclude>static/</exclude>-->
                     <exclude>html/</exclude>
                     <exclude>upload/</exclude>
-                    <exclude>template/</exclude>-->
+                    <exclude>template/</exclude>
 
                     <!-- 如果生产需要实时修改WEB-INF/下的页面可,启用这行并手动将项目中的WEB-INF目录复制到运行环境 -->
-                    <!-- <exclude>WEB-INF/</exclude> -->
+                     <exclude>WEB-INF/</exclude>
                 </excludes>
             </resource>
             <resource>

+ 6 - 0
src/main/java/net/mingsoft/MSApplication.java

@@ -21,12 +21,15 @@
 
 package net.mingsoft;
 
+import net.mingsoft.people.action.web.PeopleAction;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.Banner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.web.servlet.ServletComponentScan;
 import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.FilterType;
 import org.springframework.core.env.Environment;
 
 import java.util.logging.Logger;
@@ -34,6 +37,9 @@ import java.util.logging.Logger;
 @SpringBootApplication(scanBasePackages = {"net.mingsoft"})
 @MapperScan(basePackages={"**.dao","com.baomidou.**.mapper"})
 @ServletComponentScan(basePackages = {"net.mingsoft"})
+@ComponentScan(excludeFilters = @ComponentScan.Filter(
+		type = FilterType.ASSIGNABLE_TYPE,
+		classes = {PeopleAction.class}))
 public class MSApplication {
 	public static void main(String[] args) {
 		SpringApplication springApplication = new SpringApplication(MSApplication.class);

+ 190 - 186
src/main/java/net/mingsoft/config/ShiroConfig.java

@@ -7,10 +7,10 @@
  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  * the Software, and to permit persons to whom the Software is furnished to do so,
  * subject to the following conditions:
-
+ * <p>
  * The above copyright notice and this permission notice shall be included in all
  * copies or substantial portions of the Software.
-
+ * <p>
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
@@ -19,10 +19,6 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
-
-
-
-
 package net.mingsoft.config;
 
 import jakarta.annotation.Resource;
@@ -35,6 +31,12 @@ import net.mingsoft.basic.strategy.ILoginStrategy;
 import net.mingsoft.basic.strategy.IModelStrategy;
 import net.mingsoft.basic.strategy.ManagerLoginStrategy;
 import net.mingsoft.basic.strategy.ManagerModelStrategy;
+import net.mingsoft.people.action.web.WxCustomUserNamePasswordToken;
+import net.mingsoft.people.filter.PeopleLoginFilter;
+import net.mingsoft.people.realm.PeopleAuthRealm;
+import net.mingsoft.people.realm.PeopleLoginMD5CredentialsMatcher;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
 import org.apache.shiro.authc.Authenticator;
 import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
 import org.apache.shiro.mgt.SecurityManager;
@@ -58,185 +60,187 @@ import java.util.Map;
 @Configuration
 public class ShiroConfig {
 
-	@Autowired(required = false)
-	MSProperties msProperties;
-
-	@Resource
-	ServerProperties serverProperties;
-
-	/**
-	 * 开启Shiro的注解(如@RequiresRoles , @RequiresPermissions),需借助SspringAOP扫描使用Sshiro注解的类,并在必要时进行安全逻辑验证
-	 * 配置以下两个bean(Defaul tAdvisorAutoProxyCreator和uthorizat ionAttributeSourceAdvisor)即可实现此功能
-	 */
-	@Bean
-	public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
-		DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
-		advisorAutoProxyCreator.setProxyTargetClass(true);
-		return advisorAutoProxyCreator;
-	}
-
-	/**
-	 * 开启shiro aop注解支持
-	 * 使用代理方式;所以需要开启代码支持
-	 * @param securityManager
-	 */
-	@Bean
-	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
-		AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
-		authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
-		return authorizationAttributeSourceAdvisor;
-	}
-
-	@Bean
-	public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
-			@Autowired(required = false) DefaultWebSecurityManager securityManager) {
-		AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
-		advisor.setSecurityManager(securityManager);
-		return advisor;
-	}
-
-	@Bean
-	public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
-		DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
-		autoProxyCreator.setProxyTargetClass(true);
-		return autoProxyCreator;
-	}
-
-	@Bean(name = "shiroFilterFactoryBean")
-	public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
-		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
-		// 必须设置 SecurityManager
-		shiroFilterFactoryBean.setSecurityManager(securityManager);
-		// setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
-		shiroFilterFactoryBean.setLoginUrl(MSProperties.manager.path + "/login.do");
-		// 设置无权限时跳转的 url;
-		shiroFilterFactoryBean.setUnauthorizedUrl(MSProperties.manager.path + "/404.do");
-
-
-		Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
-		filters.put("authc",new ShiroLoginFilter());
-		// 依赖会员后需放开104行,107行
-		// PeopleLoginFilter会员登录过滤器,在这里people/**接口都会拦截校验是否登录
-		// filters.put("pauth",new PeopleLoginFilter(MSProperties.people.loginUrl));
-		// 角色校验过滤器最终会在对应的reaml中的hasRole校验,可以在ShiroRoleFilter中自定义一些操作
-		 filters.put("managerRoles", new ShiroRoleFilter(MSProperties.manager.path  + "/login.do"));
-		// filters.put("peopleRoles", new ShiroRoleFilter(MSProperties.people.loginUrl));
-
-		// 设置拦截器
-		Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
-		// 游客,开发权限
-		filterChainDefinitionMap.put("/static/**", "anon");
-		filterChainDefinitionMap.put("/html/**", "anon");
-		// 开放登陆接口
-		filterChainDefinitionMap.put(MSProperties.manager.path + "/login.do", "anon");
-		filterChainDefinitionMap.put(MSProperties.manager.path + "/checkLogin.do", "anon");
-		// 其余接口一律拦截
-		// 主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
-		// 依赖会员后,放开123行以及202-205行
-		// roles[**]中的**值必须和对应的Reaml赋予当前登录的Roles名称一致,然后值必须时CustomUserNamePasswordToken.AuthType中的值,如果这里值和对应Reaml值不一致则会出现无法访问情况
-		filterChainDefinitionMap.put(msProperties.getManager().path + "/**", "authc,managerRoles[MANAGER]");
-		// filterChainDefinitionMap.put("/people/**", "pauth,peopleRoles[PEOPLE]");
-
-		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
-		return shiroFilterFactoryBean;
-	}
-
-	/**
-	 * 注入 securityManager
-	 */
-	@Bean("securityManager")
-	public DefaultWebSecurityManager securityManager(List<Realm> realms, Authenticator authenticator, DefaultWebSessionManager sessionManager) {
-		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
-		securityManager.setSessionManager(sessionManager);
-		securityManager.setAuthenticator(authenticator);
-		//集群环境下使用redis共享session用下行代码
+    @Autowired(required = false)
+    MSProperties msProperties;
+
+    @Resource
+    ServerProperties serverProperties;
+
+    /**
+     * 开启Shiro的注解(如@RequiresRoles , @RequiresPermissions),需借助SspringAOP扫描使用Sshiro注解的类,并在必要时进行安全逻辑验证
+     * 配置以下两个bean(Defaul tAdvisorAutoProxyCreator和uthorizat ionAttributeSourceAdvisor)即可实现此功能
+     */
+    @Bean
+    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+        advisorAutoProxyCreator.setProxyTargetClass(true);
+        return advisorAutoProxyCreator;
+    }
+
+    /**
+     * 开启shiro aop注解支持
+     * 使用代理方式;所以需要开启代码支持
+     * @param securityManager
+     */
+    @Bean
+    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
+        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
+        return authorizationAttributeSourceAdvisor;
+    }
+
+    @Bean
+    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor(
+            @Autowired(required = false) DefaultWebSecurityManager securityManager) {
+        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
+        advisor.setSecurityManager(securityManager);
+        return advisor;
+    }
+
+    @Bean
+    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
+        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
+        autoProxyCreator.setProxyTargetClass(true);
+        return autoProxyCreator;
+    }
+
+    @Bean(name = "shiroFilterFactoryBean")
+    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
+        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
+        // 必须设置 SecurityManager
+        shiroFilterFactoryBean.setSecurityManager(securityManager);
+        // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
+        shiroFilterFactoryBean.setLoginUrl(MSProperties.manager.path + "/login.do");
+        // 设置无权限时跳转的 url;
+        shiroFilterFactoryBean.setUnauthorizedUrl(MSProperties.manager.path + "/404.do");
+        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
+        filters.put("authc", new ShiroLoginFilter());
+        // 依赖会员后需放开104行,107行
+        // PeopleLoginFilter会员登录过滤器,在这里people/**接口都会拦截校验是否登录
+        filters.put("pauth", new PeopleLoginFilter(MSProperties.people.loginUrl));
+        // 角色校验过滤器最终会在对应的reaml中的hasRole校验,可以在ShiroRoleFilter中自定义一些操作
+        filters.put("managerRoles", new ShiroRoleFilter(MSProperties.manager.path + "/login.do"));
+        filters.put("peopleRoles", new ShiroRoleFilter(MSProperties.people.loginUrl));
+        // 设置拦截器
+        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
+        // 游客,开发权限
+        filterChainDefinitionMap.put("/static/**", "anon");
+        filterChainDefinitionMap.put("/html/**", "anon");
+        // 开放登陆接口
+        filterChainDefinitionMap.put(MSProperties.manager.path + "/login.do", "anon");
+        filterChainDefinitionMap.put(MSProperties.manager.path + "/checkLogin.do", "anon");
+        // 其余接口一律拦截
+        // 主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
+        // 依赖会员后,放开123行以及202-205行
+        // roles[**]中的**值必须和对应的Reaml赋予当前登录的Roles名称一致,然后值必须时CustomUserNamePasswordToken.AuthType中的值,如果这里值和对应Reaml值不一致则会出现无法访问情况
+        filterChainDefinitionMap.put(msProperties.getManager().path + "/**", "authc,managerRoles[MANAGER]");
+        filterChainDefinitionMap.put("/people/**", "pauth,peopleRoles[PEOPLE]");
+        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
+        return shiroFilterFactoryBean;
+    }
+
+    /**
+     * 注入 securityManager
+     */
+    @Bean("securityManager")
+    public DefaultWebSecurityManager securityManager(List<Realm> realms, Authenticator authenticator, DefaultWebSessionManager sessionManager) {
+        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
+        securityManager.setSessionManager(sessionManager);
+        securityManager.setAuthenticator(authenticator);
+        // 集群环境下使用redis共享session用下行代码
 //        securityManager.setCacheManager(shiroRedisCacheManager);
-		// 设置realm
-		securityManager.setRealms(realms);
-		return securityManager;
-	}
-
-	/**
-	 * 重写defaultWebSessionManager,防止url拼接jsessionid
-	 * @return
-	 */
-	@Bean
-	public DefaultWebSessionManager defaultWebSessionManager() {
-		DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
-		if(serverProperties.getServlet().getSession().getTimeout()!=null) {
-			//单位毫秒
-			sessionManager.setGlobalSessionTimeout(serverProperties.getServlet().getSession().getTimeout().getSeconds()*1000L);
-		}
-		sessionManager.setSessionDAO(getMemorySessionDAO());
-		sessionManager.setSessionIdCookie(getSimpleCookie());
-		sessionManager.setSessionIdUrlRewritingEnabled(false);
-		return sessionManager;
-	}
-
-	/**
-	 * 身份验证器
-	 * @return Authenticator
-	 */
-	@Bean
-	public Authenticator authenticator() {
-		CustomModularRealmAuthenticator modularRealmAuthenticator = new CustomModularRealmAuthenticator();
-		modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
-		return modularRealmAuthenticator;
-	}
-
-
-	@Bean
-	public MemorySessionDAO getMemorySessionDAO() {
-		return new MemorySessionDAO();
-	}
-
-	@Bean
-	public SimpleCookie getSimpleCookie() {
-		SimpleCookie simpleCookie = new SimpleCookie();
-		simpleCookie.setName(msProperties.getCookieName());
-		return simpleCookie;
-
-	}
-
-
-
-	/**
-	 * 自定义身份认证 realm;
-	 * <p>
-	 * 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm, 否则会影响 CustomRealm类 中其他类的依赖注入
-	 */
-	@Bean
-	public ManagerAuthRealm customRealm() {
-		return new ManagerAuthRealm();
-	}
-
-
-	/**
-	 * 自定义会员身份认证realm,依赖会员后放开202-205行
-	 * @return
-	 */
-//	@Bean("peopleAuth")
-//	public PeopleAuthRealm peopleAuthRealm() {
-//		return new PeopleAuthRealm();
-//	}
-
-	/**
-	 * 管理员菜单策略
-	 *
-	 * @return
-	 */
-	@Bean
-	public IModelStrategy modelStrategy() {
-		return  new ManagerModelStrategy();
-	}
-
-	/**
-	 * 管理登录策略
-	 *
-	 * @return
-	 */
-	@Bean
-	public ILoginStrategy loginStrategy() {
-		return new ManagerLoginStrategy();
-	}
+        // 设置realm
+        securityManager.setRealms(realms);
+        return securityManager;
+    }
+
+    /**
+     * 重写defaultWebSessionManager,防止url拼接jsessionid
+     * @return
+     */
+    @Bean
+    public DefaultWebSessionManager defaultWebSessionManager() {
+        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
+        if (serverProperties.getServlet().getSession().getTimeout() != null) {
+            // 单位毫秒
+            sessionManager.setGlobalSessionTimeout(serverProperties.getServlet().getSession().getTimeout().getSeconds() * 1000L);
+        }
+        sessionManager.setSessionDAO(getMemorySessionDAO());
+        sessionManager.setSessionIdCookie(getSimpleCookie());
+        sessionManager.setSessionIdUrlRewritingEnabled(false);
+        return sessionManager;
+    }
+
+    /**
+     * 身份验证器
+     * @return Authenticator
+     */
+    @Bean
+    public Authenticator authenticator() {
+        CustomModularRealmAuthenticator modularRealmAuthenticator = new CustomModularRealmAuthenticator();
+        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
+        return modularRealmAuthenticator;
+    }
+
+    @Bean
+    public MemorySessionDAO getMemorySessionDAO() {
+        return new MemorySessionDAO();
+    }
+
+    @Bean
+    public SimpleCookie getSimpleCookie() {
+        SimpleCookie simpleCookie = new SimpleCookie();
+        simpleCookie.setName(msProperties.getCookieName());
+        return simpleCookie;
+    }
+
+    /**
+     * 自定义身份认证 realm;
+     * <p>
+     * 必须写这个类,并加上 @Bean 注解,目的是注入 CustomRealm, 否则会影响 CustomRealm类 中其他类的依赖注入
+     */
+    @Bean
+    public ManagerAuthRealm customRealm() {
+        return new ManagerAuthRealm();
+    }
+
+    /**
+     * 自定义会员身份认证realm,依赖会员后放开202-205行
+     * @return
+     */
+    @Bean("peopleAuth")
+    public PeopleAuthRealm peopleAuthRealm() {
+        PeopleAuthRealm peopleAuthRealm = new PeopleAuthRealm();
+        peopleAuthRealm.setCredentialsMatcher(new PeopleLoginMD5CredentialsMatcher() {
+            @Override
+            public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
+                if (token instanceof WxCustomUserNamePasswordToken) {
+                    // 微信登录,忽略密码验证
+                    return true;
+                }
+                return super.doCredentialsMatch(token, info);
+            }
+        });
+        return peopleAuthRealm;
+    }
+
+    /**
+     * 管理员菜单策略
+     *
+     * @return
+     */
+    @Bean
+    public IModelStrategy modelStrategy() {
+        return new ManagerModelStrategy();
+    }
+
+    /**
+     * 管理登录策略
+     *
+     * @return
+     */
+    @Bean
+    public ILoginStrategy loginStrategy() {
+        return new ManagerLoginStrategy();
+    }
 }

+ 827 - 0
src/main/java/net/mingsoft/people/action/web/PeopleAction.java

@@ -0,0 +1,827 @@
+/**
+ * Copyright (c) 2012-present 铭软科技(mingsoft.net)
+ * 本软件及相关文档文件(以下简称“软件”)的版权归 铭软科技 所有
+ * 遵循 铭软科技《服务协议》中的《保密条款》
+ */
+package net.mingsoft.people.action.web;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Validator;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.crypto.SecureUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import net.mingsoft.base.entity.ResultData;
+import net.mingsoft.base.exception.BusinessException;
+import net.mingsoft.basic.realm.CustomUserNamePasswordToken;
+import net.mingsoft.basic.util.BasicUtil;
+import net.mingsoft.basic.util.RestTemplateUtil;
+import net.mingsoft.basic.util.StringUtil;
+import net.mingsoft.mdiy.util.ConfigUtil;
+import net.mingsoft.people.action.BaseAction;
+import net.mingsoft.people.action.people.PeopleUserAction;
+import net.mingsoft.people.annotation.PeopleLogAnn;
+import net.mingsoft.people.bean.PeopleBean;
+import net.mingsoft.people.bean.PeopleLoginBean;
+import net.mingsoft.people.biz.IPeopleBiz;
+import net.mingsoft.people.biz.IPeopleUserBiz;
+import net.mingsoft.people.constant.e.PeopleEnum;
+import net.mingsoft.people.constant.e.PeopleLogTypeEnum;
+import net.mingsoft.people.constant.e.SessionConstEnum;
+import net.mingsoft.people.entity.PeopleEntity;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.apache.shiro.subject.Subject;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.bind.annotation.*;
+
+import java.sql.Timestamp;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+/**
+ * 铭飞会员模块,前端调用(不需要用户登录进行的操作)
+ *
+ * @author 铭飞开发团队
+ * @version 版本号:0.0<br/>
+ * 创建日期:2017-8-23 10:10:22<br/>
+ * 历史修订:<br/>
+ */
+
+/**
+ * 原来的的代码有点问题,强行覆盖
+ *
+ * @author hx
+ * @date 2025-10-20
+ */
+@Tag(name = "前端-会员模块接口")
+@Controller("webPeople")
+@RequestMapping("/")
+public class PeopleAction extends BaseAction {
+
+    /**
+     * 注入用户基础业务层
+     */
+    @Autowired
+    private IPeopleBiz peopleBiz;
+
+    /**
+     * 注入用户基础业务层
+     */
+    @Autowired
+    private IPeopleUserBiz peopleUserBiz;
+    @Autowired
+    private PeopleUserAction action;
+
+
+    @Operation(summary = "验证码验证,例如流程需要短信验证或邮箱验证,为有效防止恶意发送验证码。提供给ajax异步请求使用,注意:页面提交对验证码表单属性名称必须是rand_code,否则无效")
+    @Parameter(name = "rand_code", description = "验证码", required = true, in = ParameterIn.QUERY)
+    @PostMapping(value = "/checkCode")
+    @ResponseBody
+    public ResultData checkCode(HttpServletRequest request, HttpServletResponse response) {
+        // 验证码验证 验证码不为null 或 验证码不相等
+        if (!checkRandCode()) {
+            return ResultData.build().error(
+                    this.getResString("err.error", this.getResString("rand.code")));
+        }
+        return ResultData.build().success();
+    }
+
+    @Operation(summary = "验证用户名、手机号、邮箱是否可用,同一时间只能判断一种,优先用户名称 ,只验证已绑定的用户,建议独立使用")
+    @Parameters({
+            @Parameter(name = "peopleMail", description = "用户邮箱", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peoplePhone", description = "用户电话", required = false, in = ParameterIn.QUERY)
+    })
+    @PostMapping(value = "/check")
+    @ResponseBody
+    public ResultData check(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request, HttpServletResponse response) {
+        PeopleEntity _people = peopleBiz.getByPeople(people);
+        if (_people != null) {
+            return ResultData.build().success();
+        } else {
+            return ResultData.build().error();
+        }
+    }
+
+    @Operation(summary = "微信-登录")
+    @Parameters({
+            @Parameter(name = "peoplePassword", description = "用户密码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = true, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/wx_checkLogin")
+    @ResponseBody
+    @PeopleLogAnn(title = "登录", businessType = PeopleLogTypeEnum.LOGIN)
+    public ResultData wxCheckLogin(String code, HttpServletRequest request,
+                                   HttpServletResponse response) {
+        return Optional.ofNullable(code)
+                .map(c -> "https://api.weixin.qq.com/sns/jscode2session?appid=wx0a115eb69d6e9359&secret=d906c8ca51acfd61e13dd61b62364050&js_code=" + c + "&grant_type=authorization_code")
+                .map(HttpUtil::get)
+                .map(JSONObject::new)
+                .map(json -> json.getStr("openid"))
+                .filter(StringUtils::isNotBlank)
+                .map(openid -> {
+                    Map<String, String> map = new HashMap<>();
+                    map.put("openid", openid);
+                    PeopleEntity user = this.peopleBiz.getEntityByUserName(openid);
+                    if (user != null) {
+                        return executeLogin(openid,request,response);
+                    }
+                    return ResultData.build().success(map);
+                })
+                .orElse(ResultData.build().error("登录失败"));
+    }
+
+    @Operation(summary = "登录验证,登录必须存在验证码")
+    @Parameters({
+            @Parameter(name = "peoplePassword", description = "用户密码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "rand_code", description = "验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleAutoLogin", description = " 大于0表示自动登录", required = false, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/checkLogin")
+    @ResponseBody
+    @PeopleLogAnn(title = "登录", businessType = PeopleLogTypeEnum.LOGIN)
+    public ResultData checkLogin(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                                 HttpServletResponse response) {
+        // 是否开启了登录功能
+        String switchLogin = ConfigUtil.getString("会员配置", "enableLogin");
+        if ("false".equalsIgnoreCase(switchLogin)) {
+            return ResultData.build().error("登录功能已被关闭!");
+        }
+        // 验证码验证 验证码不为null 或 验证码不相等
+        if (!this.checkRandCode()) {
+            return ResultData.build().error(this.getResString("err.error", this.getResString("rand.code")));
+        }
+        // 用户名和密码不能为空
+        if (StringUtils.isBlank(people.getPeopleName()) || StringUtils.isBlank(people.getPeoplePassword())) {
+            return ResultData.build().error(
+                    this.getResString("err.error", this.getResString("people")));
+        }
+        PeopleEntity peopleEntity = this.peopleBiz.getEntityByUserName(people.getPeopleName());
+        if (peopleEntity == null) {
+            // 用户名或密码错误
+            return ResultData.build().error(
+                    this.getResString("err.error", this.getResString("people.no.exist")));
+        }
+        Subject subject = SecurityUtils.getSubject();
+        CustomUserNamePasswordToken cupt = new CustomUserNamePasswordToken(peopleEntity.getPeopleName(), people.getPeoplePassword(), CustomUserNamePasswordToken.AuthType.PEOPLE);
+        try {
+            LOG.debug("people 尝试登陆");
+            subject.login(cupt);
+            LOG.debug("people 登陆成功");
+            peopleEntity.setPeopleIp(BasicUtil.getIp());
+            peopleBiz.updateById(peopleEntity);
+            PeopleLoginBean tempPeople = new PeopleLoginBean();
+            tempPeople.setId(peopleEntity.getId());
+            tempPeople.setPeopleName(peopleEntity.getPeopleName());
+            tempPeople.setPeopleMail(peopleEntity.getPeopleMail());
+            tempPeople.setPeopleState(peopleEntity.getPeopleState()); // 状态
+            return ResultData.build().success(tempPeople);
+        } catch (Exception e) {
+            LOG.debug("people 登陆失败");
+            if (e.getCause() instanceof BusinessException) {
+                throw (BusinessException) e.getCause();
+            }
+            e.printStackTrace();
+        }
+        return ResultData.build().error(
+                this.getResString("err.error", this.getResString("people.no.exist")));
+    }
+
+    @Operation(summary = "验证用户是否登录")
+    @PostMapping(value = "/checkLoginStatus")
+    @ResponseBody
+    public ResultData checkLoginStatus(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            PeopleEntity people = this.getPeopleBySession();
+            return ResultData.build().success();
+        } catch (UnauthorizedException e) {
+            return ResultData.build().error();
+        }
+    }
+
+    @Operation(summary = "验证重置密码过程中收到的验证码是否正确")
+    @Parameters({
+            @Parameter(name = "peopleCode", description = "用户随机验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "rand_code", description = "验证码", required = true, in = ParameterIn.QUERY)
+    })
+    @PostMapping(value = "/checkResetPasswordCode")
+    @ResponseBody
+    @PeopleLogAnn(title = "修改密码", businessType = PeopleLogTypeEnum.UPDATE)
+    public ResultData checkResetPasswordCode(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                                             HttpServletResponse response) {
+        // 验证码验证 验证码不为null 或 验证码不相等
+        if (StringUtils.isBlank(this.getRandCode()) || !this.checkRandCode()) {
+            return ResultData.build().error(this.getResString("err.error", this.getResString("rand.code")));
+        }
+        PeopleEntity _people = (PeopleEntity) BasicUtil.getSession(SessionConstEnum.PEOPLE_EXISTS_SESSION);
+        if (_people == null) {
+            // 用户不存在
+            return ResultData.build().error(
+                    this.getResString("err.not.exist", this.getResString("people")));
+        }
+        LOG.debug(_people.getPeoplePhoneCheck() + ":" + PeopleEnum.PHONE_CHECK.toInt());
+        LOG.debug(_people.getPeopleCode() + ":" + people.getPeopleCode());
+        // 判断用户验证是否通过\判断用户输入对邮箱验证码是否与系统发送对一致\判断验证码对有效时间
+        if (_people.getPeoplePhoneCheck() == PeopleEnum.PHONE_CHECK.toInt()
+                && _people.getPeopleCode().equals(people.getPeopleCode())) {
+            BasicUtil.setSession(SessionConstEnum.PEOPLE_RESET_PASSWORD_SESSION, _people);
+            return ResultData.build().success(
+                    this.getResString("success", this.getResString("people.get.password")));
+        } else if (_people.getPeopleMailCheck() == PeopleEnum.MAIL_CHECK.toInt()
+                && _people.getPeopleCode().equals(people.getPeopleCode())) {
+            BasicUtil.setSession(SessionConstEnum.PEOPLE_RESET_PASSWORD_SESSION, _people);
+            return ResultData.build().success(
+                    this.getResString("success", this.getResString("people.get.password")));
+        } else {
+            return ResultData.build().error(
+                    this.getResString("fail", this.getResString("people.get.password")));
+        }
+    }
+
+    @Operation(summary = "用户名、邮箱、手机号验证 ,用户重置密码必须使用该接口,适用场景:1、用户注册是对用户名、邮箱或手机号唯一性判断 2、用户取回密码是判断账号是否存在")
+    @Parameters({
+            @Parameter(name = "peoplePhone", description = "用户电话", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleMail", description = "用户邮箱", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = true, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/isExists")
+    @ResponseBody
+    public ResultData isExists(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                               HttpServletResponse response) {
+        LOG.debug(JSONUtil.toJsonStr(people));
+        if (StringUtils.isBlank(people.getPeopleName()) && StringUtils.isBlank(people.getPeoplePhone())
+                && StringUtils.isBlank(people.getPeopleMail())) {
+            return ResultData.build().error(
+                    this.getResString("err.empty", this.getResString("people.name")));
+        }
+        // 如果接收到mail值,就给mail_check赋值1
+        if (!StringUtils.isBlank(people.getPeopleMail())) {
+            people.setPeopleMailCheck(PeopleEnum.MAIL_CHECK);
+        }
+        // 如果接收到phone值,就给phone_check赋值1
+        if (!StringUtils.isBlank(people.getPeoplePhone())) {
+            people.setPeoplePhoneCheck(PeopleEnum.PHONE_CHECK);
+        }
+        PeopleEntity _people = (PeopleEntity) this.peopleBiz.getEntity(people);
+        if (_people != null) {
+            BasicUtil.setSession(SessionConstEnum.PEOPLE_EXISTS_SESSION, _people);
+            return ResultData.build().success();
+        }
+        return ResultData.build().error();
+    }
+
+    @Operation(summary = "微信-用户注册")
+    @Parameters({
+            @Parameter(name = "peoplePassword", description = "登录密码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleCode", description = "用户随机验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peoplePhone", description = "用户电话", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleMail", description = "用户邮箱", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = false, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/wx_register")
+    @ResponseBody
+    public ResultData wxRegister(@RequestBody PeopleBean people, HttpServletRequest request,
+                                 HttpServletResponse response) {
+        people.setPeoplePassword(RandomUtil.randomString(8));
+        ResultData register = register(people, request, response);
+        if (register.isSuccess()) {
+            return executeLogin(people.getPeopleName(),request,response);
+        }
+        return register;
+    }
+
+    private ResultData executeLogin(String openid,HttpServletRequest request, HttpServletResponse response) {
+        Subject subject = SecurityUtils.getSubject();
+        WxCustomUserNamePasswordToken cupt = new WxCustomUserNamePasswordToken(openid);
+        try {
+            LOG.debug("people 尝试登陆");
+            subject.login(cupt);
+            LOG.debug("people 登陆成功");
+            ResultData info = action.info(request, response);
+            if (info.isSuccess()) {
+                Map data = info.getData(Map.class);
+                PeopleBean tempPeople = new PeopleBean();
+                tempPeople.setId(peopleEntity.getId());
+                tempPeople.setPeopleName(peopleEntity.getPeopleName());
+                tempPeople.setPeoplePhone(peopleEntity.getPeoplePhone());
+                tempPeople.setPuIcon(peopleEntity.getPuIcon());
+                tempPeople.setPuNickname(peopleEntity.getPuNickname());
+                return ResultData.build().success(tempPeople);
+            }
+            return info;
+        } catch (Exception e) {
+            LOG.debug("people 登陆失败");
+            if (e.getCause() instanceof BusinessException) {
+                throw (BusinessException) e.getCause();
+            }
+            e.printStackTrace();
+        }
+        return ResultData.build().error(
+                this.getResString("err.error", this.getResString("people.no.exist")));
+    }
+
+    @Operation(summary = "用户注册,用户可以根据用名称、手机号、邮箱进行注册")
+    @Parameters({
+            @Parameter(name = "peoplePassword", description = "登录密码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleCode", description = "用户随机验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peoplePhone", description = "用户电话", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleMail", description = "用户邮箱", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = false, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/register")
+    @ResponseBody
+    public ResultData register(@ModelAttribute @Parameter(hidden = true) PeopleBean people, HttpServletRequest request,
+                               HttpServletResponse response) {
+        LOG.debug("people register");
+        // 验证码验证 验证码不为null 或 验证码不相等
+        if (!checkRandCode()) {
+            return ResultData.build().error(this.getResString("err.error", this.getResString("rand.code")));
+        }
+        // 是否开启了注册功能
+        String switchRegister = ConfigUtil.getString("会员配置", "enableReg");
+        if ("false".equalsIgnoreCase(switchRegister)) {
+            return ResultData.build().error("注册功能已被关闭!");
+        }
+        // 判断用户信息是否为空
+        if (people == null) {
+            return ResultData.build().error(
+                    this.getResString("err.empty", this.getResString("people")));
+        }
+        // 要验证用户名是否为空
+        if (StringUtils.isBlank(people.getPeopleName())) {
+            return ResultData.build().error(
+                    this.getResString("err.empty", this.getResString("people.name")));
+        } else if (!StringUtils.isBlank(people.getPeopleName())) {
+            // 如果用户名不为空表示使用的是账号注册方式
+            if (people.getPeopleName().contains(" ")) {
+                return ResultData.build().error(
+                        this.getResString("people.name") + this.getResString("people.space"));
+            }
+            if (!StringUtil.checkLength(people.getPeopleName(), 3, 30)) {
+                return ResultData.build().error(
+                        this.getResString("err.length", this.getResString("people.name"), "3", "30"));
+            }
+            // 判断用户名是否已经被注册
+            PeopleEntity peopleName = this.peopleBiz.getEntityByUserName(people.getPeopleName());
+            if (peopleName != null) {
+                return ResultData.build().error(
+                        this.getResString("err.exist", this.getResString("people.name")));
+            }
+        }
+        String sendCode = ConfigUtil.getString("会员配置", "sendCode");
+        if ("true".equalsIgnoreCase(sendCode)) {// 验证手机号
+            if (StringUtils.isBlank(people.getPeoplePhone())) {
+                return ResultData.build().error(
+                        this.getResString("err.empty", this.getResString("people.phone")));
+            }
+            PeopleEntity peoplePhone = this.peopleBiz.getEntityByUserName(people.getPeoplePhone());
+            if (peoplePhone != null && peoplePhone.getPeoplePhoneCheck() == PeopleEnum.PHONE_CHECK.toInt()) { // 已存在
+                return ResultData.build().error(
+                        this.getResString("err.exist", this.getResString("people.phone")));
+            } else {
+                Object obj = BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION);
+                if (obj != null) {
+                    PeopleEntity _people = (PeopleEntity) obj;
+                    if (_people.getPeoplePhone().equals(people.getPeoplePhone())) {
+                        if (_people.getPeopleCode().equals(people.getPeopleCode())) {
+                            people.setPeoplePhoneCheck(PeopleEnum.PHONE_CHECK);
+                        } else {
+                            return ResultData.build().error(
+                                    this.getResString("err.error", this.getResString("people.phone.code")));
+                        }
+                    }
+                }
+            }
+        }
+        if ("true".equalsIgnoreCase(sendCode)) {// 验证邮箱
+            if (StringUtils.isBlank(people.getPeoplePhone())) {
+                return ResultData.build().error(
+                        this.getResString("err.empty", this.getResString("people.mail")));
+            }
+            // 检查邮箱格式是否含有空格
+            if (people.getPeopleMail().contains(" ")) {
+                return ResultData.build().error(
+                        this.getResString("people.mail") + this.getResString("people.space"));
+            }
+            PeopleEntity peopleMail = this.peopleBiz.getEntityByUserName(people.getPeopleMail());
+            if (peopleMail != null && peopleMail.getPeopleMailCheck() == PeopleEnum.MAIL_CHECK.toInt()) {
+                return ResultData.build().error(
+                        this.getResString("err.exist", this.getResString("people.mail")));
+            } else {
+                Object obj = BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION);
+                if (obj != null) {
+                    PeopleEntity _people = (PeopleEntity) obj;
+                    if (_people.getPeopleMail().equals(people.getPeopleMail())) {
+                        if (_people.getPeopleCode().equals(people.getPeopleCode())) {
+                            people.setPeopleMailCheck(PeopleEnum.MAIL_CHECK);
+                        } else {
+                            return ResultData.build().error(
+                                    this.getResString("err.error", this.getResString("people.mail")));
+                        }
+                    }
+                } else {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            }
+        }
+        // 密码
+        if (StringUtils.isBlank(people.getPeoplePassword())) {
+            return ResultData.build().error(
+                    this.getResString("err.empty", this.getResString("people.password")));
+        }
+        if (people.getPeoplePassword().contains(" ")) {
+            return ResultData.build().error(
+                    this.getResString("people.password") + this.getResString("people.space"));
+        }
+        if (people.getPeoplePassword().length() < 6 || people.getPeoplePassword().length() > 30) {
+            return ResultData.build().error(
+                    this.getResString("err.length", this.getResString("people.password"), "6", "30"));
+        }
+        // 如果用户未设置昵称,则昵称默认为用户名
+        if (StringUtils.isBlank(people.getPuNickname())) {
+            people.setPuNickname(people.getPeopleName());
+        }
+        // 将密码使用MD5加密
+        people.setPeoplePassword(SecureUtil.md5(people.getPeoplePassword()));
+        // 是否开启审核
+        String state = ConfigUtil.getString("会员配置", "peopleState");
+        if ("false".equals(state)) {
+            people.setPeopleState(1); // 审核默认值
+        } else {
+            people.setPeopleState(0);
+        }
+        people.setPeopleDateTime(new Date());
+        peopleBiz.savePeople(people);
+        LOG.debug("people register ok");
+        return ResultData.build().success();
+    }
+
+    @Operation(summary = "用户重置密码")
+    @Parameters({
+            @Parameter(name = "peoplePassword", description = "登录密码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "rand_code", description = "验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleCode", description = "用户随机验证码", required = true, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/resetPassword")
+    @ResponseBody
+    @PeopleLogAnn(title = "重置密码", businessType = PeopleLogTypeEnum.UPDATE)
+    public ResultData resetPassword(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                                    HttpServletResponse response) {
+        // 验证码验证 验证码不为null 或 验证码不相等
+        if (StringUtils.isBlank(this.getRandCode()) || !this.checkRandCode()) {
+            return ResultData.build().error(this.getResString("err.error", this.getResString("rand.code")));
+        }
+        // 验证新密码的长度
+        if (!StringUtil.checkLength(people.getPeoplePassword(), 3, 12)) {
+            return ResultData.build().error(
+                    this.getResString("err.error", this.getResString("people.password")));
+        }
+        PeopleEntity _people = (PeopleEntity) BasicUtil.getSession(SessionConstEnum.PEOPLE_RESET_PASSWORD_SESSION);
+        if (_people == null) {
+            // 用户不存在
+            return ResultData.build().error(
+                    this.getResString("err.not.exist", this.getResString("people")));
+        }
+        // 判断用户验证是否通过\判断用户输入对邮箱验证码是否与系统发送对一致\判断验证码对有效时间
+        if (_people.getPeoplePhoneCheck() == PeopleEnum.PHONE_CHECK.toInt()
+                && _people.getPeopleCode().equals(people.getPeopleCode())) {
+            _people.setPeoplePassword(SecureUtil.md5(people.getPeoplePassword()));
+            peopleBiz.updateEntity(_people);
+            LOG.debug("更新密码成功");
+            return ResultData.build().success(
+                    this.getResString("success", this.getResString("people.get.password")));
+        } else if (_people.getPeopleMailCheck() == PeopleEnum.MAIL_CHECK.toInt()
+                && _people.getPeopleCode().equals(people.getPeopleCode())) {
+            _people.setPeoplePassword(SecureUtil.md5(people.getPeoplePassword()));
+            peopleBiz.updateEntity(_people);
+            LOG.debug("更新密码成功");
+            return ResultData.build().success(
+                    this.getResString("success", this.getResString("people.get.password")));
+        } else {
+            LOG.debug("更新密码失败");
+            return ResultData.build().error(
+                    this.getResString("fail", this.getResString("people.get.password")));
+        }
+    }
+
+    @Operation(summary = "用户发送验证码,可以通过邮箱或手机发送")
+    @Parameters({
+            @Parameter(name = "receive", description = "接收地址,只能是邮箱或手机号,邮箱需要使用邮箱插件,手机号需要短信插件", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "modelCode", description = "对应邮件插件的模块编号", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "configType", description = "配置类型,如邮件配置", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "isSession", description = "true启用session保存code,false 关联用户信息,true一般是当用户手机还不存在系统中时使用,", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "peoplePhone", description = "用户电话", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleMail", description = "用户邮箱", required = false, in = ParameterIn.QUERY),
+            @Parameter(name = "peopleName", description = "登录帐号", required = false, in = ParameterIn.QUERY),
+    })
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    @PostMapping(value = "/sendCode")
+    @ResponseBody
+    public ResultData sendCode(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                               HttpServletResponse response) {
+        if (!this.checkRandCode()) {
+            return ResultData.build().error(
+                    this.getResString("err.error", this.getResString("rand.code")));
+        }
+        String receive = request.getParameter("receive");
+        String modelCode = request.getParameter("modelCode");
+        String configType = request.getParameter("configType");
+        boolean isSession = BasicUtil.getBoolean("isSession");
+        if (StringUtils.isBlank(receive)) {
+            return ResultData.build().error(this.getResString("err.empty", this.getResString("receive")));
+        }
+        if (StringUtils.isBlank(modelCode)) {
+            return ResultData.build().error(this.getResString("err.empty", "modelCode"));
+        }
+        if (StringUtils.isBlank(configType)) {
+            return ResultData.build().error(this.getResString("err.empty", this.getResString("type")));
+        }
+        // 获取应用ID
+        String peopleCode = StringUtil.randomNumber(6);
+        // 解密得到的模块编码
+        long time = new Date().getTime();
+        BasicUtil.setSession("tokenSession", time);
+        MultiValueMap<String, Object> requestBody = new LinkedMultiValueMap<>();
+        requestBody.add("modelCode", modelCode);
+        requestBody.add("receive", receive);
+        requestBody.add("tokenSession", time);
+        requestBody.add("configType", configType);
+        Map map = new HashMap();
+        map.put("code", peopleCode);
+        requestBody.add("content", JSONUtil.toJsonStr(map));
+        this.LOG.debug("验证码:{}", peopleCode);
+        if (isSession) { // 启用session
+            Object obj = BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION);
+            if (obj != null) {
+                PeopleEntity p = (PeopleEntity) obj;
+                if (DateUtil.betweenMs(new Date(), (Date) p.getPeopleCodeSendDate()) == 60) {
+                    return ResultData.build().error(this.getResString("people.code.time.error"));
+                }
+            }
+            PeopleEntity _people = new PeopleEntity();
+            _people.setPeopleCode(peopleCode);
+            _people.setPeopleCodeSendDate(Timestamp.valueOf(DateUtil.now()));
+            _people.setPeopleMail(receive);
+            _people.setPeoplePhone(receive);
+            BasicUtil.setSession(SessionConstEnum.SEND_CODE_SESSION, _people);
+            // 调用发送插件,由于不能直接依赖发送插件,所以只能通过reset接口的方式
+            ResponseEntity<JSONObject> content = RestTemplateUtil.post(this.getUrl(request) + "/msend/send.do", RestTemplateUtil.getHeaders(request), requestBody, JSONObject.class);
+            LOG.debug("send " + receive + ":content " + peopleCode);
+            return ResultData.build().success(JSONUtil.toJsonStr(content));
+        }
+        // 给people赋值(邮箱或电话)
+        if (StringUtil.isMobile(receive)) {
+            people.setPeoplePhone(receive);
+        } else {
+            people.setPeopleMail(receive);
+        }
+        // 判断是否接到用户名,应用于找回密码发送验证码
+        PeopleEntity peopleSession = null;
+        try {
+            peopleSession = this.getPeopleBySession();
+        } catch (UnauthorizedException e) {
+            LOG.debug("当前用户未登录");
+        }
+        if (StringUtils.isBlank(people.getPeopleName()) && peopleSession == null) {
+            // 如果接收到mail值,就给mail_check赋值1
+            if (!StringUtils.isBlank(people.getPeopleMail())) {
+                people.setPeopleMailCheck(PeopleEnum.MAIL_CHECK);
+            }
+            // 如果接收到phone值,就给phone_check赋值1
+            if (!StringUtils.isBlank(people.getPeoplePhone())) {
+                people.setPeoplePhoneCheck(PeopleEnum.PHONE_CHECK);
+            }
+        }
+        // 通过用户名地址和应用id得到用户实体
+        PeopleEntity peopleEntity = (PeopleEntity) this.peopleBiz.getEntity(people);
+        if (peopleEntity == null) {
+            return ResultData.build().error(
+                    this.getResString("err.not.exist", this.getResString("people")));
+        }
+        if (peopleEntity.getPeopleUser() != null) {
+            Map _map = new HashMap();
+            _map.put("code", peopleCode);
+            _map.put("userName", peopleEntity.getPeopleUser().getPuNickname());
+            requestBody.remove("content");
+            requestBody.add("content", JSONUtil.toJsonStr(_map));
+        }
+        // 将生成的验证码加入用户实体
+        peopleEntity.setPeopleCode(peopleCode);
+        // 将当前时间转换为时间戳格式保存进people表
+        peopleEntity.setPeopleCodeSendDate(Timestamp.valueOf(DateUtil.now()));
+        // 更新该实体
+        this.peopleBiz.updateEntity(peopleEntity);
+        PeopleEntity _people = (PeopleEntity) BasicUtil.getSession(SessionConstEnum.PEOPLE_EXISTS_SESSION);
+        if (_people != null) {
+            BasicUtil.setSession(SessionConstEnum.PEOPLE_EXISTS_SESSION, peopleEntity);
+        }
+        // 调用发送插件,由于不能直接依赖发送插件,所以只能通过reset接口的方式
+        ResponseEntity<JSONObject> content = RestTemplateUtil.post(this.getUrl(request) + "/msend/send.do", RestTemplateUtil.getHeaders(request), requestBody, JSONObject.class);
+        LOG.debug("content :" + content);
+        if (content.getBody().getBool("result")) {
+            LOG.debug("send " + receive + ":content " + peopleCode);
+            return ResultData.build().success();
+        } else {
+            return ResultData.build().error(content.getBody().getStr("msg"));
+        }
+    }
+
+    @Operation(summary = "验证用户输入的接收验证码")
+    @Parameters({
+            @Parameter(name = "receive", description = "接收地址,只能是邮箱或手机号,邮箱需要使用邮箱插件,手机号需要短信插件", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "code", description = "接收到的验证码", required = true, in = ParameterIn.QUERY),
+    })
+    @PostMapping(value = "/checkSendCode")
+    @ResponseBody
+    public ResultData checkSendCode(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                                    HttpServletResponse response) {
+        String code = request.getParameter("code");
+        String receive = request.getParameter("receive");
+        Boolean isMobile = Validator.isMobile(receive);
+        Boolean isEmail = Validator.isEmail(receive);
+        if (isMobile) {
+            // 手机验证码
+            if (StringUtils.isBlank(code)) {
+                return ResultData.build().error(
+                        this.getResString("err.error", this.getResString("people.phone.code")));
+            }
+            people.setPeoplePhone(receive);
+        }
+        // 跳过邮箱验证码验证
+        if (isEmail) {
+            // 邮箱验证码
+            if (StringUtils.isBlank(code)) {
+                return ResultData.build().error(
+                        this.getResString("err.error", this.getResString("people.mail.code")));
+            }
+            people.setPeopleMail(receive);
+        }
+        // 根据邮箱地址查找用户实体
+        PeopleEntity peopleEntity = (PeopleEntity) this.peopleBiz.getEntity(people);
+        // 在注册流程,在发送验证码的时候数据库可能还不存在用户信息
+        if (BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION) != null) {
+            peopleEntity = (PeopleEntity) BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION);
+            // 判断用户输入的随机码是否正确
+            if (!peopleEntity.getPeopleCode().equals(code)) {
+                if (isMobile) {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.phone.code")));
+                } else {
+                    // 邮箱验证
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            } else {
+                return ResultData.build().success();
+            }
+        } else {
+            if (isMobile) {
+                // 如果用户已经绑定过手机直接返回错误
+                if (peopleEntity == null || peopleEntity.getPeoplePhoneCheck() == PeopleEnum.PHONE_CHECK.toInt()) {
+                    return ResultData.build().error(this.getResString("err.error", this.getResString("people.phone.code")));
+                }
+            } else {
+                // 如果用户已经绑定过邮箱直接返回错误
+                if (peopleEntity == null || peopleEntity.getPeopleMailCheck() == PeopleEnum.MAIL_CHECK.toInt()) {
+                    return ResultData.build().error(this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            }
+            // 得到发送验证码时间,并转换为String类型
+            String date = peopleEntity.getPeopleCodeSendDate().toString();
+            // 如果发送时间和当前时间只差大于30分钟,则返回false
+            if (DateUtil.betweenMs(new Date(), DateUtil.parse(date)) > 60 * 60 * 24) {
+                return ResultData.build().error(getResString("overdue", getResString("rand.code")));
+            }
+            // 判断用户输入的随机码是否正确
+            if (!peopleEntity.getPeopleCode().equals(code)) {
+                if (isMobile) {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.phone.code")));
+                } else {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            }
+            // 将随机码在数据库中清空
+            peopleEntity.setPeopleCode("");
+            if (StringUtil.isMobile(receive)) {
+                peopleEntity.setPeoplePhoneCheck(PeopleEnum.PHONE_CHECK);
+            } else {
+                peopleEntity.setPeopleMailCheck(PeopleEnum.MAIL_CHECK);
+            }
+            peopleBiz.updateEntity(peopleEntity);
+            return ResultData.build().success();
+        }
+    }
+
+    @Operation(summary = "解绑邮箱-> 验证用户输入的接收验证码")
+    @Parameters({
+            @Parameter(name = "code", description = "接收到的验证码", required = true, in = ParameterIn.QUERY),
+            @Parameter(name = "receive", description = "接收地址,只能是邮箱或手机号,邮箱需要使用邮箱插件,手机号需要短信插件", required = true, in = ParameterIn.QUERY)
+    })
+    @PostMapping(value = "/cancelBind")
+    public ResultData cancelBind(@ModelAttribute @Parameter(hidden = true) PeopleEntity people, HttpServletRequest request,
+                                 HttpServletResponse response) {
+        String code = request.getParameter("code");
+        String receive = request.getParameter("receive");
+        Boolean isMobile = Validator.isMobile(receive);
+        Boolean isEmail = Validator.isEmail(receive);
+        if (isMobile) {
+            // 验证码
+            if (StringUtils.isBlank(code)) {
+                return ResultData.build().error(
+                        this.getResString("err.error", this.getResString("people.phone.code")));
+            }
+            people.setPeoplePhone(receive);
+        }
+        if (isEmail) {
+            // 验证码
+            if (StringUtils.isBlank(code)) {
+                return ResultData.build().error(
+                        this.getResString("err.error", this.getResString("people.mail.code")));
+            }
+            people.setPeopleMail(receive);
+        }
+        // 根据用户名和邮箱或手机号查找用户实体
+        PeopleEntity peopleEntity = (PeopleEntity) this.peopleBiz.getEntity(people);
+        // 在注册流程,在发送验证码的时数据库可能还不存在用户信息
+        if (BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION) != null) {
+            peopleEntity = (PeopleEntity) BasicUtil.getSession(SessionConstEnum.SEND_CODE_SESSION);
+            // 判断用户输入的随机码是否正确
+            if (!peopleEntity.getPeopleCode().equals(code)) {
+                if (isMobile) {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.phone.code")));
+                } else {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            } else {
+                return ResultData.build().success();
+            }
+        } else {
+            if (isMobile) {
+                // 如果用户未绑定过手机直接返回错误
+                if (peopleEntity.getPeoplePhoneCheck() == PeopleEnum.PHONE_NO_CHECK.toInt()) {
+                    return ResultData.build().error();
+                }
+            } else {
+                // 如果用户未绑定过邮箱直接返回错误
+                if (peopleEntity.getPeopleMailCheck() == PeopleEnum.MAIL_NO_CHECK.toInt()) {
+                    return ResultData.build().error();
+                }
+            }
+            // 得到发送验证码时间,并转换为String类型
+            String date = peopleEntity.getPeopleCodeSendDate().toString();
+            // 如果发送时间和当前时间只差大于30分钟,则返回false
+            if (DateUtil.betweenMs(new Date(), DateUtil.parse(date)) > 60 * 60 * 24) {
+                return ResultData.build().error(getResString("overdue", getResString("rand.code")));
+            }
+            // 判断用户输入的随机码是否正确
+            if (!peopleEntity.getPeopleCode().equals(code)) {
+                if (isMobile) {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.phone.code")));
+                } else {
+                    return ResultData.build().error(
+                            this.getResString("err.error", this.getResString("people.mail.code")));
+                }
+            }
+            // 将随机码在数据库中清空
+            peopleEntity.setPeopleCode("");
+            if (StringUtil.isMobile(receive)) {
+                peopleEntity.setPeoplePhoneCheck(PeopleEnum.PHONE_NO_CHECK);
+            } else {
+                peopleEntity.setPeopleMailCheck(PeopleEnum.MAIL_NO_CHECK);
+            }
+            peopleBiz.updateEntity(peopleEntity);
+            return ResultData.build().success();
+        }
+    }
+
+    @Operation(summary = "安全退出")
+    @PostMapping(value = "/loginOut")
+    @ResponseBody
+    public ResultData loginOut(HttpServletRequest request,
+                               HttpServletResponse response) {
+        SecurityUtils.getSubject().logout();
+        return ResultData.build().success();
+    }
+}

+ 15 - 0
src/main/java/net/mingsoft/people/action/web/WxCustomUserNamePasswordToken.java

@@ -0,0 +1,15 @@
+package net.mingsoft.people.action.web;
+
+import net.mingsoft.basic.realm.CustomUserNamePasswordToken;
+
+/**
+ * 微信登录
+ *
+ * @author hx
+ * @date 2025-10-22
+ */
+public class WxCustomUserNamePasswordToken extends CustomUserNamePasswordToken {
+    public WxCustomUserNamePasswordToken(String username) {
+        super(username, null, AuthType.PEOPLE);
+    }
+}

+ 4 - 3
src/main/resources/application-dev.yml

@@ -1,7 +1,8 @@
 spring:
   datasource:
-    url: jdbc:mysql://localhost:3306/mcms?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&allowMultiQueries=true&useSSL=false
+    url: jdbc:mysql://localhost:3306/user_test?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&allowMultiQueries=true&useSSL=false
     username: root
-    password: root
+    password:
     filters: wall,mergeStat
-    type: com.alibaba.druid.pool.DruidDataSource
+    type: com.alibaba.druid.pool.DruidDataSource
+spring.main.allow-bean-definition-overriding: true

+ 1 - 1
src/main/resources/application.yml

@@ -53,7 +53,7 @@ ms:
 
   manager:
     path: /ms #后台访问的路径,如:http://项目/ms/login.do,生产的时候建议修改
-    check-code: true #默认开启验证码验证,false验证码不验证
+    check-code: false #默认开启验证码验证,false验证码不验证
 
   upload:
     enable-web: true  #启用web层的上传

+ 161 - 0
zh.puml

@@ -0,0 +1,161 @@
+@startuml
+left to right direction
+skinparam monochrome true
+
+actor "个人用户\n经销商\n参展商\n等等" as User
+actor "管理员" as SysUser
+
+rectangle "CMS + 管理系统" {
+  "CMS浏览:\n门户信息\n公告\n等等" as (cms)
+  "CMS发布:\n门户信息\n公告\n等等" as (cmsSys)
+  "短信发送服务" as (sms)
+  "用户信息" as (userInfo)
+  "楼层、展会、参展商信息" as (3info)
+
+  (专属海报-企业) <.. (分享邀请-企业) : <<包含>>
+  (sms) <.. (营销短信) : <<依赖>>
+
+  (cms) ..> (cmsSys) : <<依赖>>
+
+  User --> (cms)
+
+
+  (3info) <-- SysUser
+  (cmsSys) <-- SysUser
+  (userInfo) <-- SysUser
+  (分享邀请-企业) <-- SysUser
+  (营销短信) <-- SysUser
+}
+
+rectangle 微信服务 {
+  "实名+人脸识别" as (wx)
+  "解析身份信息" as (wx2)
+   (wx) ..> (wx2) : <<包含>>
+   (wx2) ..> (userInfo) : <<依赖>>
+}
+
+rectangle "硬件-门禁" {
+    "人脸识别" as (jcc)
+    "解析身份信息" as (jcc2)
+    (进出场) ..> (jcc) : <<包含>>
+    (jcc) ..> (jcc2) : <<包含>>
+    (jcc2) ..> (userInfo) : <<依赖>>
+}
+
+rectangle 小程序 {
+  "登录/注册" as (login)
+
+  User --> (login)
+  (login) ..> (wx) : <<依赖>>
+  (login) ..> (渠道来源) : <<包含>>
+  (login) ..> (短信验证码) : <<包含>>
+  (短信验证码) ..> (sms) : <<依赖>>
+
+  User --> (分享邀请-个人)
+  (分享邀请-个人) .> (专属海报-个人) : <<包含>>
+  User --> (专属海报-个人)  : <<浏览其它用户的海报>>
+  (专属海报-个人) ..> (邀请关系关联) : <<包含>>
+  (邀请关系关联) ..> (userInfo) : <<依赖>>
+
+  User --> (进出场)
+
+  User --> (导航)
+  (导航) ..> (地图) : <<包含>>
+  (导航) ..> (线上展馆) : <<包含>>
+  (地图) ..> (3info) : <<依赖>>
+}
+@enduml
+
+@startuml
+left to right direction
+skinparam monochrome true
+map "字典-渠道" as channel{
+    code => PK
+    name =>
+}
+map "现有CMS的用户表" as u{
+    id => PK
+    ... =>
+}
+map "邀请海报" as invitation {
+    id => PK
+    url => 海报的访问地址
+    content => 海报的内容\n是图片还是富文本?\n根据业务来定,可能还要单独的表
+    code => 邀请码?
+    user_id => 用户ID
+    type => 类型\n区别个人还是企业
+}
+invitation::user_id --> u::id
+
+map "用户扩展表" as user{
+    user_id => PK:用户ID
+    invitation_user => 邀请的用户
+    channel_code => 注册时的渠道码\n保存当时选择的渠道快照
+    channel_name => 注册时的渠道名\n保存当时选择的渠道快照
+    微信相关 => 不清楚CMS是否能接入微信\n不能的话,就要扩展微信相关字段
+}
+user::user_id --> u::id
+user::invitation_user --> u::id
+user::channel_code ..> channel::code
+user::channel_name ..> channel::name
+
+map "参展商信息" as exhibitors{
+    user_id => PK:用户ID
+    ... => 一些说明或公司介绍等字段?
+}
+exhibitors::user_id --> u::id
+
+map "展会" as act{
+    id => PK
+    start_time => 展会开始时间
+    end_time => 展会结束时间
+    ... => 一些展会的介绍等字段?
+}
+
+map "字典-展位" as booth{
+    code => PK:展位比如1-001
+    floor => 楼层
+    ... => 一些位置的介绍说明等字段?
+}
+
+map "展会详情" as act_info{
+    id => PK
+    act_id => 展会ID
+    booth_code => 展位编码
+    exhibitors_id => 参展商ID
+    ... =>
+}
+act_info::act_id --> act::id
+act_info::booth_code --> booth::code
+act_info::exhibitors_id --> exhibitors::user_id
+
+map "短信" as sms{
+    id => PK
+    message => 短信消息
+    code => 验证码,可能会有
+    user_id => 接收的用户
+    tel => 电话号码
+    status => 状态,默认1\n1:未发送\n2:发送成功\n3:发送失败
+    max_retry => 最大失败重试次数,默认3
+    retry => 已失败重试次数,默认0
+}
+
+map 现在CMS的文章表 {
+    id => PK
+    content => 文章内容
+}
+@enduml
+
+@startuml
+participant 小程序 as ui
+participant 后台 as sys
+
+ui -> sys: wx.login 登录,带上code
+sys --> ui: 返回用户在微信的唯一标识openid\n如果已经注册过用,就返回用户信息和登录标识cookie\n如果没注册过,不会有登录标识cookie
+
+ui -> sys: 注册:\nwx.authorize(授权)\nwx.getUserProfile(得到用户信息)\ngetPhoneNumber(获得手机号)\n短信验证\n带上openid
+sys --> ui: 注册的结果
+
+ui -> sys: 业务操作,带上登录标识cookie
+@enduml
+