文章目录
- 1、功能展示
- 2、前端部分:VueAPI
- 3、后端部分:Springboot
1、功能展示
大致分为用户管理,商品管理,收藏管理,购物车管理,订单管理五个模块。
2、前端部分:VueAPI
Vue 使用 axios 库进行网络请求,登录成功后将用户信息存储到localstorage中,供每次使用时调用,退出时清空信息。
import axios from "axios";
axios({
headers: {'Content-Type':'application/json'},
method: 'post',
url: 'http://xxxx:9090/user/login',
data: JSON.stringify({
username: this.username,
password: this.password,
}),
}).then(function(return_data){
const res = return_data.data;
console.log(res)
if(res.message == "success"){
_this.$message({
message: "登录成功",
type: "success",
});
//登录成功后存储用户信息
localStorage.setItem("access", JSON.stringify(res));
that.$router.push({path:'/wode'});
}else{
_this.$message.error({
message: "登录失败,用户名或密码错误",
});
}
})
将请求获得的信息通过Data返回到Temple中
export default {
data(){
var shopping = JSON.parse(window.localStorage.getItem("shopping"))
return {
list: shopping, //XX数据
}
},
methods: {
onSubmit(){ console.log(6); }, //XX函数
},
mounted () { this.getdatas() } //每次启动时执行
};
Temple中支持调用data的数据和methods函数
通过v-for遍历list,{{在页面中访问list的数据}}, @click调用函数
<div id="box2-1" v-for="(item,index) in list" :key="index">
<p id="box2-11">{{item.dianpu}}</p>
<van-button id="del" @click="onClickDel(item.name,item.dianpu)">删除</van-button>
<img v-bind:src="item.photo" alt="jumi" width="80" height="80" id="box2-12">
<p id="box2-13">{{item.name}}</p>
<p id="box2-14">{{item.jieshao}}</p>
<p id="box2-15">¥{{item.price}}</p>
</el-input-number>
</div>
配置浏览器跨越访问,请求服务端的API
//vue.config.js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
allowedHosts: "all",
port:9999,
proxy: {
'/api': {
target: "http://xxxxx.cn",
changOrigin: true, //接口跨域
pathRewrite: { '^/api': '/' }
}
}
}
})
3、后端部分:Springboot
使用的技术栈
Spring Boot,MySQL,MyBatis,Spring Security,Redis
开发工具:
-
云服务器 CentOS 7.9 位于腾讯云上海(公网展示)
-
云数据库 RDS Mysql 8.0 位于阿里云杭州(部署时无需迁移数据库)
-
本地使用 Navicat连接远程数据库并在本地进行开发和测试
-
API测试成功后将后端项目打包为jar部署到服务器上
-
API接口管理与测试采用eolink
-
后端开发使用IntelliJ IDEA和 JDK1.8 进行
业务逻辑实现
- 采用Springboot三层架构
- Controller层实现业务控制,提供API接口访问
- Service层实现业务逻辑,比如订单的创建,用户的认证和判断。
- Dao层实现数据持久化,与MyBatis的mapper一对一绑定,完成数据库CURD操作。
- entity层维护数据库表的实体类,并且其他三层中调用。
//Controller层
@Controller
public class OrderController {
// 订单服务
@Autowired
private OrderDao orderDao;
@Autowired
private ShoppingService shoppingService;
// 获取指定用户的订单
@GetMapping("/getorder")
@ResponseBody
public List<Order> getorder(@RequestParam("username") String username) {
return orderDao.getorder(username);
}
// 从购物车创建订单并删除商品
@PostMapping("/makeorder")
@ResponseBody
public boolean makeorder(@RequestBody Map<String,Object> params) {
String username = params.get("username").toString();
return shoppingService.makeorder(username);
}
}
// Service层
@Service
public class ThingServiceImpl implements ThingService {
@Autowired
private ThingDao thingDao;
@Override
public List<Thing> getthings() {
return thingDao.getthings();
}
@Override
public List<Thing> searchthings(String name){
return thingDao.searchthings("%"+name+"%");
}
@Override
public Thing idsget(int ids){
return thingDao.idsget(ids);
}
}
// Dao层
@Repository
public interface ThingDao {
// 获取全部的商品
List<Thing> getthings();
// 搜索包含对应名称的商品
List<Thing> searchthings(@Param("name") String name);
// 查询唯一商品名称
Thing querythings(@Param("name") String name);
// 根据ids获取商品
Thing idsget(@Param("ids") int ids);
}
// MyBatis
<!--绑定一个对应的Dao/Map接口-->
<mapper namespace="com.farm.dao.OrderDao">
<!--获取指定用户的订单-->
<select id="getorder" resultType="com.farm.entity.Order">
select orderid, username, mess, price, photo from orders
where username = #{username}
</select>
<!--获取最大的用户id-->
<select id="getnewid" resultType="java.lang.Integer">
select MAX(orderid) from orders limit 1
</select>
<!--从购物车创建订单-->
<insert id="makeorder" >
insert into orders(orderid, username, mess, price, photo)
values(#{orderid},#{username},#{mess}, #{price}, #{photo})
</insert>
</mapper>
安全验证的实现
- 采用Spring Security的JSON Web Token (JWT)插件进行认证与授权,在信息安全上有一定保证。
- 采用Redis数据库进行token验证,提高性能并减少计算量。
- 用户登录时生成token签名,并存储在Redis中,每次操作前检查redis中是否存在对应token。
// JWT 工具类
public static String createJWT(String jwtSec, long ttlMillis, String username) {
// 指定签名的时候使用的签名算法,也就是header那部分
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
// 创建payload的私有声明
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("username", username);
// 添加payload声明
// 设置jwt的body
JwtBuilder builder = Jwts.builder()
.setClaims(claims)
.setId(UUID.randomUUID().toString())
.setIssuedAt(now)
.setSubject(username)
.signWith(signatureAlgorithm, jwtSec.getBytes(StandardCharsets.UTF_8));
if (ttlMillis >= 0) {
long expMillis = nowMillis + ttlMillis;
Date exp = new Date(expMillis);
// 设置过期时间
builder.setExpiration(exp);
}
return builder.compact();
}
//redis 工具类
//指定缓存失效时间
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
//删除缓存
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(Arrays.asList(key));
}
}
}
//缓存获取
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
//普通缓存放入并设置时间
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
常见的API报错码
400: 语法无效, 如JSON参数不对。
415: 不支持其有效载荷的格式, 如没放请求头。
403: 资源不可用, 服务器拒绝处理,如目录访问错。
500: 内部错误,后端代码实现问题。
404: 请求不存在,如服务器没有对应API。