# shop
**Repository Path**: wanxiaoguo/shop
## Basic Information
- **Project Name**: shop
- **Description**: 软件工程课程设计二级项目—— 睿乐购电商平台
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 5
- **Forks**: 0
- **Created**: 2020-06-16
- **Last Updated**: 2023-06-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
### 项目说明
+ 该项目是燕山大学信息科学与工程学院(软件学院)的17级软件工程7,8班的软件工程课程设计二级项目
+ 项目是2020年6月16日开始,2020年7月3日答辩结束,项目不再维护
+ 项目定位在电商项目项目
### 项目说明






### 创建git 项目
+ 创建 git 仓库:
```git
mkdir shop
cd shop
git init
touch README.md
git add README.md
git commit -m "first commit"
git remote add origin https://gitee.com/wanxiaoguo/shop.git
git push -u origin master
```
+ 已有仓库导入
```git
cd existing_git_repo
git remote add origin https://gitee.com/wanxiaoguo/shop.git
git push -u origin master
```
+ git使用强推:`git push origin 分支名 --force`
### 首次使用(6.17)
+ 找一个目录起名shop
+ 使用git clone https://gitee.com/wanxiaoguo/shop.git
+ 第一种方式:在自己的电脑创建分支
+ 第二种方式:直接再master分支开发
+ 往自己的开发分支上推比如 git push origin master:dev_wan 其中origin 为远程主机名(git clone 后不需要改)master为本地分支名(直接再master就行) dev_wan:为自己需要推送到远程的分支名
+ 参考提交
```git
$ git add -A
$ git commit -m '提交信息'
$ git push origin master:dev_wan
```
### git bash使用参考
+ tab : 密码补全
+ 上键:历史命令
+ 粘贴:shift+ins
### 项目结构调整与文件说明(6.17)
+ html文件都在 src/main/resources/static目录下
+ 后台管理系统的文件位于src/main/resources/static/admin下
+ 前端编写放置在 src/main/resources/static/admin/rlg下
+ 接口编写再 src/main/java编写
+ annotation:自定义注解
+ config: 配置文件
+ controller: 控制层,接口处理
+ exception: 自定义异常类
+ mapper: 模型层,往数据库查询数据
+ popj: 数据库表对应的实体类
+ service: 业务层接口
+ impl: 接口实现类
+ util: 工具类
+ handler: 处理器
+ ShopApplication: 启动器
+ es.sql : 项目的数据库
+ 管理员账户密码:admin ysuadmin
### 命名说明与注意事项
+ 采用REST风格
+ get: 获取数据
+ post: 添加数据
+ delete: 删除数据
+ put: 修改数据
+ 操作命名
+ getUserById: 表示通过id获取用户
+ getUserList: 表示获取用户列表
+ addUser : 表示添加用户
+ delUser : 表示删除用户
+ updUser : 表示修改用户
+ By: 表示通过某些字段进行某些操作
+ Num|Count: 表示获取数据的大小
+ controller说明
+ 对于add操作,表里有的默认字段前端可以不传,mapper必须写,添加前需要校验,为空添加默认字段
+ 在对于测试的时候:使用日志输出
```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private Logger log = LoggerFactory.getLogger(UserController.class);
log.debug("输出字段信息")
```
### ajax参考
```js
function login() {
var userName = $("#username").val();
var password = $("#password").val();
if (isNull(userName)) {
alert("请输入用户名!");
return;
}
if (!validUserName(userName)) {
alert("请输入正确的用户名!");
return;
}
if (isNull(password)) {
alert("请输入密码!");
return;
}
if (!validPassword(password)) {
alert("请输入正确的密码!");
return;
}
var data = { "username": userName, "password": password }
//console.log(data);
$.ajax({
type: "POST", //方法类型
dataType: "json", //预期服务器返回的数据类型
url: "http://localhost:8082/user/login",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function(result) {
console.log(result);
if (result.status == 200) {
setCookie("token", result.data.userToken);
setCookie("username",username);
window.location.href = "index.html";
};
if (result.status == 406 ) {
alert("登陆失败!请检查账号和密码!");
return;
}
},
error: function() {
alert("接口异常,请联系管理员!");
return;
}
});
}
```
### 问题记录
+ 数据库的导入(6.18): `source sql文件位置`
+ [自定义注解解决Springboot发送post请求时string类型参数接收不到的问题(HandlerMethodArgumentResolver )](https://blog.csdn.net/CNAHYZ/article/details/105129050)
+ springboot2 前台传送json数据,后台接收数据(6.18)
+ 可以对实体类进行封装,但String不行
+ 使用JSONObject 获取json 再获取键值
+ 需要在参数前加上`@RequestBody`注解
### jquery 获取表单数据
+ jquery 获取 获取select标签值
+ 元素内容 `$('#searchId').find('option:selected').text()`
+ 根据name `$("select[name='searchId'] option:selected").val();`
+ 根据id ` $("#searchId option:selected").val()`
+ 设置value为pxx的项选中 `$("#searchId").val("pxx");`
+ 设置text为pxx的项选中 `$("#searchId").find("option:contains('pxx')").attr("selected",true);`
+
### 前后端分离方案
#### session与cookie
+ Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。
+ Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有 效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。
+ Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。
#### token
+ 首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间 生成 token ,返回给前端
+ 前端拿到后端返回的 token ,存储在 localStroage 里
+ 赋值:`localStorage.setItem("token", result.data.token);` | `localStorage.token=` | `localStorage[token]=`
+ 读取信息:`localStorage.getItem("token")` | `localStorage.token` | `localStorage[token]`
+ 删除localStorage指定键对应的值:`localStorage.removeItem('token');`
+ 删除localStorage所有值: `localStorage.clear();`
+ js使用cookie保存token(cookie在http请求中,随着请求发送到服务器)
+ 保存token: `sessionStorage.setItem("token","value")`;
+ 获取token: `sessionStorage.getItem("token")`
+ 删除: `sessionStorage.removeItem("key"); `
+ 删除保存的所有数据: `sessionStorage.clear()`
+ 前端每次路由跳转, 判断 localStroage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态
+ 每次请求接口,在 请求头里携带 token
```js
$.ajax({
type: "post",
url: areaComp,
dataType: "json",
contentType: "application/json",
data: data,
async: false,
//再次添加头部信息
beforeSend: function(request) {
request.setRequestHeader("token", token);
},
success: function (data){})
```
+ 后端接口 判断 请求头有无 token,没有或者 token 过期,返回401
+ 前端得到 401 状态码,重定向 到登录页面
+ 与cookie相比较的优势:
+ 支持跨域访问 ,将token置于请求头中,而cookie是不支持跨域访问的;
+ 无状态化, 服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session;
+ 无需绑定到一个特殊的身份验证 方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可;
+ 更适用于移动端 (Android,iOS,小程序等等),像这种原生平台不支持cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以每次去手动为他添加cookie,详情请查看博主另一篇博客;
+ 避免CSRF跨站伪造攻击 ,还是因为不依赖cookie;
+ 非常适用于RESTful API ,这样可以轻易与各种后端(java,.net,python…)相结合,去耦合
##### 参考代码
+ cookie操作相关方法
```js
/**
* 写入cookie
*
* @param name
* @param value
*/
function setCookie(name, value) {
var Days = 30;
var exp = new Date();
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/";
}
/**
* 读取cookie
* @param name
* @returns {null}
*/
function getCookie(name) {
var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
if (arr = document.cookie.match(reg))
return unescape(arr[2]);
else
return null;
}
/**
* 删除cookie
* @param name
*/
function delCookie(name) {
var exp = new Date();
exp.setTime(exp.getTime() - 1);
if (cval != null)
document.cookie = name + "=" + "xxxx;expires=" + exp.toUTCString();
}
/**
* 检查cookie
*/
function checkCookie() {
if (getCookie("token") == null) {
window.location.href = "login.html";
}
}
/**
* 检查cookie
*/
function checkResultCode(code) {
if (code == 402) {
window.location.href = "login.html";
}
}
```
+ jquery简写
+ 先引入jquery,再引入:jquery.cookie.js;下载:http://plugins.jquery.com/cookie/
```js
// 设置cookie expires是保存时间,单位是天 path : 能够访问的页面 / 表示根路径(全部页面都能访问)
$.cookie('token', 'token_value', { expires: 10, path: '/' });
// 获取cookie
$.cookie("token")
// 删除cookie
$.cookie("名称",null)
$.cookie('the_cookie','the_value',{
expires:7, //expires:(Number|Date)有效期;设置一个整数时,单位是天;也可以设置一个日期对象作为Cookie的过期日期;
path:'/', //path:(String)创建该Cookie的页面路径
domain:'jquery.com', //path:(String)创建该Cookie的页面路径
secure:true //(Booblean)如果设为true,那么此Cookie的传输会要求一个安全协议,例如:HTTPS;
})
```
+ login
```js
function login() {
var userName = $("#username").val();
var password = $("#password").val();
var data = { "username": userName, "password": password }
//console.log(data);
$.ajax({
type: "POST", //方法类型
dataType: "json", //预期服务器返回的数据类型
url: "http://localhost:8082/user/login",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(data),
success: function(result) {
console.log(result);
if (result.status == 200) {
setCookie("token", result.data.userToken);
setCookie("username",username);
window.location.href = "index.html";
};
if (result.status == 406 ) {
alert("登陆失败!请检查账号和密码!");
return;
}
},
error: function() {
alert("接口异常,请联系管理员!");
return;
}
});
}
```
+ 对于某些页面必须登录才能访问,使用文档加载事件
```html
```
+ ajax请求是携带token,比如删除用户
```js
function userDel() {
$.ajax({
type: "DELETE",
url: "users/delete/1",
contentType: "application/json",
beforeSend: function(request) {
//设置header值
request.setRequestHeader("token", getCookie("token"));
},
success: function(r) {
if (r.resultCode == 200) {
console.log("删除成功")
} else {
console.log("删除失败")
}
}
});
}
```
### 接口注意事项
+ 必须登录 `@TokenToUser User oldUser`
+ 先判断参数
+ `getErrorResult`
+ `code, msg`
+ `1, msg`
+ `getSuccessResult`
+ 非String,传数据 (data)
+ 传String msg (msg)
+ 传data `(Object)`
+ 非实体类
+ `JSONObject jsonObject = new JSONObject()`;
`jsonObject.put("question",user.getQuestion())`;
```java
@RestController
public class AdminManagerController {
@Resource
private UserService userService;
@GetMapping("/user/list")
public Result getUsers(@RequestParam(required = false) Map params, @TokenToUser User oldUser){
if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) {
return ResultGenerator.getErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!");
}
if (oldUser==null){
return ResultGenerator.getErrorResult(Constants.RESULT_CODE_NOT_LOGIN,"用户未登录,请登录");
}
if (!"A".equals(oldUser.getRole())){
return ResultGenerator.getErrorResult("没有权限");
}
PageUtil pageUtil = new PageUtil(params);
System.out.println(params);
return ResultGenerator.getSuccessResult(userService.getUserPage(pageUtil));
}
@PostMapping("/user/login")
public Result login(@RequestBody JSONObject jsonParam){
String username = jsonParam.getString("username");
String password = jsonParam.getString("password");
}
@PostMapping("/register")
public Result register(@RequestBody User user){
}
}
```
+ sql语句
+ update():SET 最后加 update_time=NOW()
+ 传参:#{username}
+ 命名问题:方法名不能重复
### mybatis注解使用
```java
//单参数查询注解
@Select("select * from t_user where id = #{id}")
public User getUserById(Integer id);
//多参数查询注解 当 #{} 的变量与@Param()一致默认为变量名
@Select("select * from t_user where username like CONCAT('%',#{name},'%') and employeeNum like CONCAT('%',#{num},'%')")
public List searchUsers(@Param("name") String name,@Param("num") String num);
// 属性变量有类的注解
@Select("select * from t_job where id = #{jobId}")
@Results({
@Result(property="javabean变量",column="数据库字段名",one=@One(select="全类名"))
})
public Job getJobById(Integer jobId);
//参数为Map的注解,返回值为类。
@Select("select m.user_name as userName from m_user m,m_job j where m.user_id=m.id and j.job_id=#{jobId}")
public List getJobByMap(Map map);
//参数为Map,返回值为List