# 谷粒商城 **Repository Path**: ElzatAlim/gulimall ## Basic Information - **Project Name**: 谷粒商城 - **Description**: 谷粒商城 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2021-10-06 - **Last Updated**: 2024-06-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README **docker启动**      systemctl start docker service docker start **重启docker服务** systemctl restart  docker sudo service docker restart **关闭docke**r   systemctl stop docker service docker stop **查看是否启动成功** docker ps -a Docker MYSQL 启动命令(-v是挂在 前面的真是目录 后者是镜像目录) docker run -p 3306:3306 --name mysql -v /mydata/mysql/log:/var/log/mysql -v /mydata/mysql/data:/var/lib/mysql -v /mydata/mysql/conf:/etc/mysql -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7 **查看Docker正在运行中的镜像** docker ps **进入Docker镜像交互** docker exec -it mysql(镜像名称或者是ID) /bin/bash **配置Mysql配置文件** [client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve **重启Dockers镜像** docker restart mysql **下载Redis镜像** docker pull redis **创建文件** touch redis.conf **启动Redis实例** docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf -d redis redis-server /etc/redis/redis.conf **进入dockers中的redis-cli** docker exec -it redis redis-cli **重启redis** docker restrat redis **redis持久配置** 配置文件中添加一行 appendonly yes 再重启 (应该是开启AOF持久) **docker镜像自动启动** sudo docker update mysql --restart=always **npm安装依赖提示Error: Can't find Python executable "python", you can set the PYTHON env variable** **参考** https://www.cnblogs.com/fxwoniu/p/13846586.html https://blog.csdn.net/m0_49230198/article/details/117863312 其他错误可以先不管 试着npm run dev sass安装失败的问题 切换成淘宝镜像 再下载 npm install node-sass@4.13.1 版本从package.jason中查看 **Vue生命周期** Vue 实例生命周期 **GateWay统一配置跨域配置** ```java @Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); // 请求方法 *支持所有方法 config.addAllowedMethod("*"); //跨域处理 *允许所有的域 // config.addAllowedOrigin("http://127.0.0.1:3625"); config.addAllowedOrigin("*"); // 请求头 *支持所有 config.addAllowedHeader("*"); // 是否允许携带Cookie进行跨域 config.setAllowCredentials(true); //匹配所有请求 source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } } ``` **MybatisPlus逻辑删除配置** 1.配置(高版本可以省略) ```yml mybatis-plus: global-config: db-config: logic-delete-value: 1 logic-not-delete-value: 0 ``` 2.在字段上面加上TableLogic **JSR303后端校验技术** 1.给校验Bean添加校验数据 ​ javax.validation.constraints 包下的注解 2.在控制层添加@Valid注解 开启校验 image-20211102204700289 3.配置返回校验后的错误信息 image-20211103153538994 **统一处理异常(JSR303)** 1.控制层只保留@Valid注解 2.新建一个枚举类 用于错误信息 ```java public enum BizCodeEnume { UNKNOW_EXCEPTION(10000,"系统未知异常"), VALID_EXCEPTION(10001,"参数格式校验异常"); private int code; private String msg; BizCodeEnume(int code,String msg){ this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } } ``` 3.编写错误捕获类 ```java @Slf4j //@ResponseBody //@ControllerAdvice(basePackages = "com.aili.gulimall.product.controller") @RestControllerAdvice(basePackages = "com.aili.gulimall.product.controller") public class GuliMallExceptionControllerAdvice { @ExceptionHandler(value = MethodArgumentNotValidException.class) public R handleVaildException(MethodArgumentNotValidException exception) { BindingResult bindingResult = exception.getBindingResult(); if (bindingResult.hasErrors()) { Map map = new HashMap<>(); bindingResult.getFieldErrors().forEach((item) -> { map.put(item.getField(), item.getDefaultMessage()); }); log.error("数据校验出现问题", map); return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg()).put("data", map); } log.error("数据校验出现问题"); return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg()); } @ExceptionHandler(value = Throwable.class) public R handleException(Throwable throwable) { return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg()); } } ``` **JSR303 分组校验功能** 1.创建两个接口 做为分组 ![image-20211103235929868](C:\Users\ElzatAlim\AppData\Roaming\Typora\typora-user-images\image-20211103235929868.png) 2.实体类校验注解中添加groups属性 ```java ........... public class BrandEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 品牌id */ @NotNull(message = "修改时必须指定品牌ID",groups = {UpdateGroup.class}) @Null(message = "新增不能指定Id",groups = {AddGroup.class}) @TableId private Long brandId; /** * 品牌名 */ @NotBlank(message = "品牌名不能为空",groups = {UpdateGroup.class,AddGroup.class}) private String name; ......... } ``` 3.控制层@Valid注解替换成@Validated,并且参数指定分组接口 ``` /** * 保存 */ @RequestMapping("/save") public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand ){ brandService.save(brand); return R.ok(); } ``` 注意:没有指定分组的校验规则只在没有指定分组的@Valid注解才会生效 **JSR303自定义检验** 1.编写一个自定义的校验注解(参考其他校验注解) ```java package com.aili.common.valid; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; /** * @author 艾力 * @date 2021/11/4 14:01 **/ @Documented @Constraint(validatedBy = {ListValueConstraintValidator.class}) @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public @interface ListValue { String message() default "{com.aili.common.valid.ListValue.message}"; Class[] groups() default {}; Class[] payload() default {}; int[] vals() default {}; } ``` 提示配置文件 ValidationMessages.properties ```properties com.aili.common.valid.ListValue.message = 必须提交指定的值 ``` 2.编写一个自定义的检验器 ```java package com.aili.common.valid; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.lang.annotation.Annotation; import java.util.HashSet; import java.util.Set; /** * @author 艾力 * @date 2021/11/4 14:15 **/ public class ListValueConstraintValidator implements ConstraintValidator { private Set set = new HashSet<>(); //初始化 @Override public void initialize(ListValue constraintAnnotation) { int[] vals = constraintAnnotation.vals(); for (int val : vals) { set.add(val); } ConstraintValidator.super.initialize(constraintAnnotation); } //校验 @Override public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) { return set.contains(integer); } } ``` 3.关联自定义的校验器校验带自定义的检验注解的参数 自定义注解里面配置 ```java @Constraint(validatedBy = {ListValueConstraintValidator.class}) ``` 注:配置文件乱码可以将转换成Unicode再配置 在线转换网站:http://tool.chinaz.com/tools/unicode.aspx **Vue父子组件** 被导入的组件给父组件发送事件 ```javascript methods: { //每次点击的时候 handleNodeClick(data,node,component) { console.log(data,node,component); //向父组件发送事件 this.$emit('tree-node-click', data,node,component); }, ......... } ``` 再父组件绑定事件 ```html ..... ``` **Vue组件之间发布和订阅事件(另一种)** 使用pubsub-js来实现 1.安装PubSub 首先在终端使用一下指令安装PubSub ``` npm install --save pubsub-js ``` 安装成功就能在这里看到pubsub-js 2.引入组件 在要使用的组件中引入pubsub-js,这个不能全局引入只能哪个组件中使用就在哪个组件中引入 3.消息订阅 在被调用的组件中使用 PubSub.subscribe(name,function)订阅消息 被调用组件 4.发布 在调用组件使用 PubSub.publish(name,data);发布消息 **Json返回过滤** 实体类字段上添加注解JsonInclude 如果这个集合空的话就不会返回空集合 image-20211107200008321 **MybatisPlus分页插件配置** 编写Config类 ```java @Configuration @EnableTransactionManagement //开启事务 @MapperScan("com.aili.gulimall.product.dao") public class MybatisConfig { //引入分页插件 // 旧版 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false paginationInterceptor.setOverflow(true); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInterceptor.setLimit(-1); // 开启 count 的 join 优化,只针对部分 left join paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } } ``` **远程调用微服务接口** 1.被调用的微服务启动类添加@EnableDiscoveryClient服务注册与发现注解 2.调用的微服务启动类添加@EnableFeignClients 注解开启远程调用 3.编写接口 image-20211119152005036 **统一配置时间返回格式** image-20211123022917106 **MybatisPlus queryWrapper大于等于** 原符号 < <= > >= <> 对应函数 lt() le() gt() ge() ne() Mybatis-plus写法: queryWrapper.ge("create_time", localDateTime); Mybatis写法: where create_time >= #{localDateTime} **MybatisPlus 自动补充修改时间和创建时间** 编写类 ```java import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; /** * @author 艾力 * @date 2021/1/12 19:25 **/ @Component public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //类中的属性名称 this.setFieldValByName("createTime", new Date(), metaObject); this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { this.setFieldValByName("updateTime", new Date(), metaObject); } } ``` 实体类中添加注解: image-20211129234948343 **库存服务 API详解** 领取采购单 http://127.0.0.1:88/api/ware/purchase/received body(采购单ID): ```json [13] ``` 完成采购 http://127.0.0.1:88/api/ware/purchase/done body: ```json {"id":12,"items":[ {"itemId":11,"status":3,"reason":""}, {"itemId":12,"status":3,"reason":""}, {"itemId":13,"status":3,"reason":""}, {"itemId":14,"status":3,"reason":""}, {"itemId":15,"status":3,"reason":""}, {"itemId":16,"status":3,"reason":""}, {"itemId":17,"status":3,"reason":""}, {"itemId":18,"status":3,"reason":""}, {"itemId":19,"status":3,"reason":""}, {"itemId":20,"status":3,"reason":""}, {"itemId":21,"status":3,"reason":""} ]} ```