commit
3a49cbd41b
@ -0,0 +1,8 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<annotationProcessing>
|
||||
<profile name="Maven default annotation processors profile" enabled="true">
|
||||
<sourceOutputDir name="target/generated-sources/annotations" />
|
||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||
<outputRelativeToContentRoot value="true" />
|
||||
<module name="newdemo" />
|
||||
</profile>
|
||||
</annotationProcessing>
|
||||
</component>
|
||||
<component name="JavacSettings">
|
||||
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
|
||||
<module name="newdemo" options="-parameters" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="wx_miniapp@localhost" uuid="c67a94ed-025b-4c2b-ae24-d0e09eb2c892">
|
||||
<driver-ref>mysql.8</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mysql://localhost:3306/wx_miniapp</jdbc-url>
|
||||
<jdbc-additional-properties>
|
||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
||||
</jdbc-additional-properties>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RemoteRepositoriesConfiguration">
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Central Repository" />
|
||||
<option name="url" value="https://repo.maven.apache.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="central" />
|
||||
<option name="name" value="Maven Central repository" />
|
||||
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="jboss.community" />
|
||||
<option name="name" value="JBoss Community repository" />
|
||||
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" />
|
||||
</project>
|
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.0.12</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<groupId>com.learning</groupId>
|
||||
<artifactId>newdemo</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>newdemo</name>
|
||||
<description>微信小程序登录Demo</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<mybatis.version>3.0.1</mybatis.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<jwt.version>0.11.5</jwt.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis -->
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>${mybatis.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL -->
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- FastJSON -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jwt.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
@ -0,0 +1,14 @@
|
||||
package com.learning.newdemo;
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("com.learning.newdemo.mapper")
|
||||
public class NewDemoApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(NewDemoApplication.class, args);
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.learning.newdemo.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* 跨域配置
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
// 允许所有域名进行跨域调用
|
||||
config.addAllowedOriginPattern("*");
|
||||
// 允许跨域发送cookie
|
||||
config.setAllowCredentials(true);
|
||||
// 放行全部原始头信息
|
||||
config.addAllowedHeader("*");
|
||||
// 允许所有请求方法跨域调用
|
||||
config.addAllowedMethod("*");
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.learning.newdemo.config;
|
||||
|
||||
import com.learning.newdemo.common.Result;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 处理所有未捕获的异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
public Result<Void> handleException(Exception e) {
|
||||
log.error("系统异常", e);
|
||||
return Result.error("系统异常,请联系管理员");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理运行时异常
|
||||
*/
|
||||
@ExceptionHandler(RuntimeException.class)
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
public Result<Void> handleRuntimeException(RuntimeException e) {
|
||||
log.error("运行时异常", e);
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.learning.newdemo.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* RestTemplate配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package com.learning.newdemo.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
@Data
|
||||
public class WxUser implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String openid;
|
||||
|
||||
private String nickname;
|
||||
|
||||
private String avatarUrl;
|
||||
|
||||
private Integer gender;
|
||||
|
||||
private String country;
|
||||
|
||||
private String province;
|
||||
|
||||
private String city;
|
||||
|
||||
private String language;
|
||||
|
||||
private Date createTime;
|
||||
|
||||
private Date updateTime;
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.learning.newdemo.mapper;
|
||||
|
||||
import com.learning.newdemo.entity.WxUser;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
public interface WxUserMapper {
|
||||
|
||||
/**
|
||||
* 通过openid查询用户
|
||||
*
|
||||
* @param openid 微信用户唯一标识
|
||||
* @return 用户信息
|
||||
*/
|
||||
WxUser selectByOpenid(@Param("openid") String openid);
|
||||
|
||||
/**
|
||||
* 插入新用户
|
||||
*
|
||||
* @param wxUser 用户信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int insert(WxUser wxUser);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
*
|
||||
* @param wxUser 用户信息
|
||||
* @return 影响行数
|
||||
*/
|
||||
int updateByPrimaryKey(WxUser wxUser);
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.learning.newdemo.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.learning.newdemo.entity.WxUser;
|
||||
import com.learning.newdemo.mapper.WxUserMapper;
|
||||
import com.learning.newdemo.service.WxUserService;
|
||||
import com.learning.newdemo.util.JwtUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* 微信用户服务实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class WxUserServiceImpl implements WxUserService {
|
||||
|
||||
@Value("${wechat.miniapp.appid}")
|
||||
private String appid;
|
||||
|
||||
@Value("${wechat.miniapp.secret}")
|
||||
private String secret;
|
||||
|
||||
@Autowired
|
||||
private WxUserMapper wxUserMapper;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@Override
|
||||
public String login(String code) {
|
||||
try {
|
||||
// 微信登录凭证校验接口
|
||||
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid
|
||||
+ "&secret=" + secret
|
||||
+ "&js_code=" + code
|
||||
+ "&grant_type=authorization_code";
|
||||
|
||||
ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
|
||||
JSONObject json = JSONObject.parseObject(response.getBody());
|
||||
|
||||
// 获取openid
|
||||
String openid = json.getString("openid");
|
||||
if (openid == null || openid.isEmpty()) {
|
||||
log.error("获取openid失败: {}", json);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 根据openid获取或创建用户
|
||||
getUserByOpenid(openid);
|
||||
|
||||
// 生成jwt token
|
||||
return jwtUtil.generateToken(openid);
|
||||
} catch (Exception e) {
|
||||
log.error("微信登录异常", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public WxUser getUserByOpenid(String openid) {
|
||||
WxUser wxUser = wxUserMapper.selectByOpenid(openid);
|
||||
if (wxUser == null) {
|
||||
// 如果用户不存在,创建新用户
|
||||
wxUser = new WxUser();
|
||||
wxUser.setOpenid(openid);
|
||||
wxUserMapper.insert(wxUser);
|
||||
wxUser = wxUserMapper.selectByOpenid(openid);
|
||||
}
|
||||
return wxUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateUser(WxUser wxUser) {
|
||||
if (wxUser == null || wxUser.getId() == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int rows = wxUserMapper.updateByPrimaryKey(wxUser);
|
||||
return rows > 0;
|
||||
} catch (Exception e) {
|
||||
log.error("更新用户信息异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
# 主要配置在application.yml中
|
||||
# 此文件用于避免IDE报错
|
@ -0,0 +1,30 @@
|
||||
spring:
|
||||
application:
|
||||
name: newdemo
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: 1234
|
||||
|
||||
mybatis:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
# 微信小程序配置
|
||||
wechat:
|
||||
miniapp:
|
||||
appid: wxf1f6d7657e01d48a
|
||||
secret: fc356336a118366f27c384079688bc15
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: yoursecretkey123456789abcdefghijklmnopqrstuvwxyz
|
||||
expiration: 86400000 # 24小时(毫秒)
|
||||
|
||||
# 服务端口配置
|
||||
server:
|
||||
port: 8080
|
||||
address: 0.0.0.0
|
@ -0,0 +1,22 @@
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS wx_miniapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
-- 使用数据库
|
||||
USE wx_miniapp;
|
||||
|
||||
-- 创建微信用户表
|
||||
CREATE TABLE IF NOT EXISTS `wx_user` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`openid` varchar(100) NOT NULL COMMENT '微信openid',
|
||||
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
|
||||
`avatar_url` varchar(500) DEFAULT NULL COMMENT '头像URL',
|
||||
`gender` tinyint(4) DEFAULT NULL COMMENT '性别 0-未知 1-男 2-女',
|
||||
`country` varchar(50) DEFAULT NULL COMMENT '国家',
|
||||
`province` varchar(50) DEFAULT NULL COMMENT '省份',
|
||||
`city` varchar(50) DEFAULT NULL COMMENT '城市',
|
||||
`language` varchar(50) DEFAULT NULL COMMENT '语言',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_openid` (`openid`) COMMENT 'openid唯一索引'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信用户表';
|
@ -0,0 +1,58 @@
|
||||
<?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.learning.newdemo.mapper.WxUserMapper">
|
||||
<resultMap id="BaseResultMap" type="com.learning.newdemo.entity.WxUser">
|
||||
<id column="id" property="id" jdbcType="INTEGER"/>
|
||||
<result column="openid" property="openid" jdbcType="VARCHAR"/>
|
||||
<result column="nickname" property="nickname" jdbcType="VARCHAR"/>
|
||||
<result column="avatar_url" property="avatarUrl" jdbcType="VARCHAR"/>
|
||||
<result column="gender" property="gender" jdbcType="INTEGER"/>
|
||||
<result column="country" property="country" jdbcType="VARCHAR"/>
|
||||
<result column="province" property="province" jdbcType="VARCHAR"/>
|
||||
<result column="city" property="city" jdbcType="VARCHAR"/>
|
||||
<result column="language" property="language" 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, openid, nickname, avatar_url, gender, country, province, city, language, create_time, update_time
|
||||
</sql>
|
||||
|
||||
<select id="selectByOpenid" resultMap="BaseResultMap" parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from wx_user
|
||||
where openid = #{openid,jdbcType=VARCHAR}
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="com.learning.newdemo.entity.WxUser" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into wx_user (
|
||||
openid, nickname, avatar_url, gender, country, province, city, language, create_time
|
||||
)
|
||||
values (
|
||||
#{openid,jdbcType=VARCHAR},
|
||||
#{nickname,jdbcType=VARCHAR},
|
||||
#{avatarUrl,jdbcType=VARCHAR},
|
||||
#{gender,jdbcType=INTEGER},
|
||||
#{country,jdbcType=VARCHAR},
|
||||
#{province,jdbcType=VARCHAR},
|
||||
#{city,jdbcType=VARCHAR},
|
||||
#{language,jdbcType=VARCHAR},
|
||||
now()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateByPrimaryKey" parameterType="com.learning.newdemo.entity.WxUser">
|
||||
update wx_user
|
||||
set nickname = #{nickname,jdbcType=VARCHAR},
|
||||
avatar_url = #{avatarUrl,jdbcType=VARCHAR},
|
||||
gender = #{gender,jdbcType=INTEGER},
|
||||
country = #{country,jdbcType=VARCHAR},
|
||||
province = #{province,jdbcType=VARCHAR},
|
||||
city = #{city,jdbcType=VARCHAR},
|
||||
language = #{language,jdbcType=VARCHAR},
|
||||
update_time = now()
|
||||
where id = #{id,jdbcType=INTEGER}
|
||||
</update>
|
||||
</mapper>
|
@ -0,0 +1,2 @@
|
||||
# 主要配置在application.yml中
|
||||
# 此文件用于避免IDE报错
|
@ -0,0 +1,30 @@
|
||||
spring:
|
||||
application:
|
||||
name: newdemo
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/wx_miniapp?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
|
||||
username: root
|
||||
password: 1234
|
||||
|
||||
mybatis:
|
||||
mapper-locations: classpath:mapper/*.xml
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
map-underscore-to-camel-case: true
|
||||
|
||||
# 微信小程序配置
|
||||
wechat:
|
||||
miniapp:
|
||||
appid: wxf1f6d7657e01d48a
|
||||
secret: fc356336a118366f27c384079688bc15
|
||||
|
||||
# JWT配置
|
||||
jwt:
|
||||
secret: yoursecretkey123456789abcdefghijklmnopqrstuvwxyz
|
||||
expiration: 86400000 # 24小时(毫秒)
|
||||
|
||||
# 服务端口配置
|
||||
server:
|
||||
port: 8080
|
||||
address: 0.0.0.0
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS wx_miniapp DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
|
||||
-- 使用数据库
|
||||
USE wx_miniapp;
|
||||
|
||||
-- 创建微信用户表
|
||||
CREATE TABLE IF NOT EXISTS `wx_user` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`openid` varchar(100) NOT NULL COMMENT '微信openid',
|
||||
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
|
||||
`avatar_url` varchar(500) DEFAULT NULL COMMENT '头像URL',
|
||||
`gender` tinyint(4) DEFAULT NULL COMMENT '性别 0-未知 1-男 2-女',
|
||||
`country` varchar(50) DEFAULT NULL COMMENT '国家',
|
||||
`province` varchar(50) DEFAULT NULL COMMENT '省份',
|
||||
`city` varchar(50) DEFAULT NULL COMMENT '城市',
|
||||
`language` varchar(50) DEFAULT NULL COMMENT '语言',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_openid` (`openid`) COMMENT 'openid唯一索引'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信用户表';
|
@ -0,0 +1,58 @@
|
||||
<?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.learning.newdemo.mapper.WxUserMapper">
|
||||
<resultMap id="BaseResultMap" type="com.learning.newdemo.entity.WxUser">
|
||||
<id column="id" property="id" jdbcType="INTEGER"/>
|
||||
<result column="openid" property="openid" jdbcType="VARCHAR"/>
|
||||
<result column="nickname" property="nickname" jdbcType="VARCHAR"/>
|
||||
<result column="avatar_url" property="avatarUrl" jdbcType="VARCHAR"/>
|
||||
<result column="gender" property="gender" jdbcType="INTEGER"/>
|
||||
<result column="country" property="country" jdbcType="VARCHAR"/>
|
||||
<result column="province" property="province" jdbcType="VARCHAR"/>
|
||||
<result column="city" property="city" jdbcType="VARCHAR"/>
|
||||
<result column="language" property="language" 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, openid, nickname, avatar_url, gender, country, province, city, language, create_time, update_time
|
||||
</sql>
|
||||
|
||||
<select id="selectByOpenid" resultMap="BaseResultMap" parameterType="java.lang.String">
|
||||
select
|
||||
<include refid="Base_Column_List"/>
|
||||
from wx_user
|
||||
where openid = #{openid,jdbcType=VARCHAR}
|
||||
</select>
|
||||
|
||||
<insert id="insert" parameterType="com.learning.newdemo.entity.WxUser" useGeneratedKeys="true" keyProperty="id">
|
||||
insert into wx_user (
|
||||
openid, nickname, avatar_url, gender, country, province, city, language, create_time
|
||||
)
|
||||
values (
|
||||
#{openid,jdbcType=VARCHAR},
|
||||
#{nickname,jdbcType=VARCHAR},
|
||||
#{avatarUrl,jdbcType=VARCHAR},
|
||||
#{gender,jdbcType=INTEGER},
|
||||
#{country,jdbcType=VARCHAR},
|
||||
#{province,jdbcType=VARCHAR},
|
||||
#{city,jdbcType=VARCHAR},
|
||||
#{language,jdbcType=VARCHAR},
|
||||
now()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateByPrimaryKey" parameterType="com.learning.newdemo.entity.WxUser">
|
||||
update wx_user
|
||||
set nickname = #{nickname,jdbcType=VARCHAR},
|
||||
avatar_url = #{avatarUrl,jdbcType=VARCHAR},
|
||||
gender = #{gender,jdbcType=INTEGER},
|
||||
country = #{country,jdbcType=VARCHAR},
|
||||
province = #{province,jdbcType=VARCHAR},
|
||||
city = #{city,jdbcType=VARCHAR},
|
||||
language = #{language,jdbcType=VARCHAR},
|
||||
update_time = now()
|
||||
where id = #{id,jdbcType=INTEGER}
|
||||
</update>
|
||||
</mapper>
|
@ -0,0 +1,21 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "uni-preset-vue",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev:h5": "uni",
|
||||
"dev:h5:ssr": "uni --ssr",
|
||||
"dev:mp-weixin": "uni -p mp-weixin",
|
||||
"build:h5": "uni build",
|
||||
"build:h5:ssr": "uni build --ssr",
|
||||
"build:mp-weixin": "uni build -p mp-weixin"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dcloudio/uni-app": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-app-harmony": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-app-plus": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-components": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-h5": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-alipay": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-baidu": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-jd": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-kuaishou": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-lark": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-qq": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-toutiao": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-mp-xhs": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-quickapp-webview": "3.0.0-4040520250104002",
|
||||
"vue": "^3.4.21",
|
||||
"vue-i18n": "^9.1.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@dcloudio/types": "^3.4.8",
|
||||
"@dcloudio/uni-automator": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-cli-shared": "3.0.0-4040520250104002",
|
||||
"@dcloudio/uni-stacktracey": "3.0.0-4040520250104002",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-4040520250104002",
|
||||
"@vue/runtime-core": "^3.4.21",
|
||||
"vite": "5.2.8"
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
"miniprogramRoot": "",
|
||||
"libVersion": "3.0.2"
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/// <reference types='@dcloudio/types' />
|
||||
import 'vue'
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
type Hooks = App.AppInstance & Page.PageInstance;
|
||||
|
||||
interface ComponentCustomOptions extends Hooks {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<script>
|
||||
export default {
|
||||
onLaunch: function () {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow: function () {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide: function () {
|
||||
console.log('App Hide')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/*每个页面公共css */
|
||||
</style>
|
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<view class="argument-component">
|
||||
<view class="content-card">
|
||||
<view class="card-title">立论功能</view>
|
||||
<view class="card-content">
|
||||
在这里可以进行辩题立论和论点整理
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.argument-component {
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 20rpx;
|
||||
padding: 30rpx;
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 36rpx;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<view class="tab-bar">
|
||||
<!-- 添加移动的背景块 -->
|
||||
<view class="active-bg" :style="{ transform: `translateX(${100 * props.currentComponentIndex}%)` }"></view>
|
||||
|
||||
<view
|
||||
class="tab-item"
|
||||
:class="{
|
||||
'active': props.currentComponentIndex === index,
|
||||
'is-first-tab': index === 0
|
||||
}"
|
||||
v-for="(item, index) in tabList"
|
||||
:key="index"
|
||||
@click="changeTab(index)">
|
||||
<text :class="{'active': props.currentComponentIndex === index}">{{ item.pageName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
// 定义emit事件
|
||||
const emit = defineEmits(['change-tab', 'change-index']);
|
||||
const props = defineProps(['currentComponentIndex']);
|
||||
|
||||
const tabList = [
|
||||
{
|
||||
pageName:'首页',
|
||||
component:'PageOfHome'
|
||||
},
|
||||
{
|
||||
pageName:'立论',
|
||||
component:'PageOfArgument'
|
||||
},
|
||||
{
|
||||
pageName:'复盘',
|
||||
component:'PageOfReview'
|
||||
},
|
||||
{
|
||||
pageName:'辩论',
|
||||
component:'PageOfDebate'
|
||||
}
|
||||
];
|
||||
|
||||
const changeTab = (index) => {
|
||||
if(props.currentComponentIndex === index) return;
|
||||
// 触发事件并传递当前组件名称和索引
|
||||
emit('change-tab', tabList[index].component);
|
||||
emit('change-index', index);
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tab-bar {
|
||||
display: flex;
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
height: 70%;
|
||||
background-color: #1A2C42;
|
||||
border: none;
|
||||
position: relative;
|
||||
border-radius: 25rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 15rpx rgba(0, 0, 0, 0.2);
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
}
|
||||
|
||||
/* 移动的背景块 */
|
||||
.active-bg {
|
||||
position: absolute;
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
background-color: #00D6B9;
|
||||
border-radius: 25rpx;
|
||||
transition: transform 0.3s ease;
|
||||
z-index: 1;
|
||||
box-shadow: 0 0 20rpx rgba(0, 214, 185, 0.5);
|
||||
/* 添加硬件加速 */
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tab-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 1px;
|
||||
height: 60%;
|
||||
z-index: 2;
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgba(0, 214, 185, 0.1) 15%,
|
||||
rgba(0, 214, 185, 0.5) 50%,
|
||||
rgba(0, 214, 185, 0.1) 85%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.tab-item.is-first-tab::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.tab-item text {
|
||||
font-size: 24rpx;
|
||||
color: rgba(0, 214, 185, 0.7);
|
||||
transition: all 0.3s ease;
|
||||
text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.tab-item text.active {
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1rpx 8rpx rgba(255, 255, 255, 0.4);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,10 @@
|
||||
import {
|
||||
createSSRApp
|
||||
} from "vue";
|
||||
import App from "./App.vue";
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App);
|
||||
return {
|
||||
app,
|
||||
};
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
{
|
||||
"name" : "智辨云枢",
|
||||
"appid" : "wxf1f6d7657e01d48a",
|
||||
"description" : "智辨云枢",
|
||||
"versionName" : "1.0.0",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
"app-plus" : {
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
"splashscreen" : {
|
||||
"alwaysShowBeforeRender" : true,
|
||||
"waiting" : true,
|
||||
"autoclose" : true,
|
||||
"delay" : 0
|
||||
},
|
||||
"modules" : {},
|
||||
"distribute" : {
|
||||
"android" : {
|
||||
"permissions" : [
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
|
||||
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
|
||||
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
|
||||
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
|
||||
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
|
||||
"<uses-feature android:name=\"android.hardware.camera\"/>",
|
||||
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
|
||||
]
|
||||
},
|
||||
"ios" : {},
|
||||
"sdkConfigs" : {}
|
||||
}
|
||||
},
|
||||
"quickapp" : {},
|
||||
"mp-weixin" : {
|
||||
"appid" : "wxf1f6d7657e01d48a",
|
||||
"setting" : {
|
||||
"urlCheck" : false
|
||||
},
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-alipay" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-baidu" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"mp-toutiao" : {
|
||||
"usingComponents" : true
|
||||
},
|
||||
"uniStatistics": {
|
||||
"enable": false
|
||||
},
|
||||
"vueVersion" : "3"
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/login/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "登录"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTitleText": "智辩云枢",
|
||||
"navigationBarBackgroundColor": "#1a2a6c",
|
||||
"backgroundColor": "#000000"
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export {};
|
||||
|
||||
declare module "vue" {
|
||||
type Hooks = App.AppInstance & Page.PageInstance;
|
||||
interface ComponentCustomOptions extends Hooks {}
|
||||
}
|
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 392 KiB |
@ -0,0 +1,8 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import uni from '@dcloudio/vite-plugin-uni'
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
uni(),
|
||||
],
|
||||
})
|
Loading…
Reference in new issue