Compare commits

...

115 Commits

Author SHA1 Message Date
方建军 e946f1d7b6 Merge pull request '合并' (#5) from develop into main
10 months ago
方建军 79540badd7 Merge pull request 'tangchaunxing' (#4) from tangchuanxing_branch into develop
10 months ago
方建军 988cf84554 Merge pull request 'wangjinhao' (#3) from wangjinhao_branch into develop
10 months ago
方建军 67e50d624b Merge pull request 'fangjianjun' (#2) from FangJianJun_branch into develop
10 months ago
pyuv9atf8 c6edb60936 Merge pull request 'liuzeyu' (#1) from LiiuZeYu_branch into develop
10 months ago
CR7 5e2327cce2 提交
10 months ago
CR7 b5953fadbb 提交
10 months ago
CR7 50bb228c23 提交
10 months ago
CR7 9922ab43a8 提交
10 months ago
CR7 ee73702d22 提交
10 months ago
CR7 4df9185dd8 提交
10 months ago
CR7 2ff1e41a17 提交
10 months ago
CR7 69db425b2e 提交
10 months ago
CR7 5c2ea01afc 提交
10 months ago
CR7 0ba47f9361 提交
10 months ago
CR7 582414af9d 提交
10 months ago
CR7 d9b5d43575 提交
10 months ago
CR7 5ce83e1a26 提交
10 months ago
CR7 0f71af50d1 提交
10 months ago
CR7 6d65c85869 提交
10 months ago
CR7 193c03408d 提交
10 months ago
CR7 ff5d5aa8c1 提交
10 months ago
CR7 07bb375518 提交
10 months ago
CR7 a902a9da7a 提交
10 months ago
CR7 c3c95db9e0 提交
10 months ago
CR7 dc558d7aaa 提交
10 months ago
lzy 6807ab51c4 提交
10 months ago
lzy 143989191c 提交
10 months ago
lzy 590a8ff6b2 提交
10 months ago
lzy 58291f167d 提交
10 months ago
lzy 1dc9ea7ad3 提交
10 months ago
lzy 71b960cc0b 提交
10 months ago
lzy 0badd3fdcf 提交
10 months ago
lzy 089bb1f584 提交
10 months ago
lzy 8c83805490 提交
10 months ago
lzy f3420c36aa 提交
10 months ago
lzy 656c6fc449 提交
10 months ago
lzy 57cbf1bd64 提交
10 months ago
lzy 60e2da7b5b 提交
10 months ago
方建军 2e37262a8b 提交
10 months ago
lzy ce088810be 提交
10 months ago
lzy 488914ffc5 提交
10 months ago
lzy f4b1f98cc4 提交
10 months ago
lzy ffcb1a13fb 提交
10 months ago
lzy 14cecf1f57 提交
10 months ago
lzy 3e1c52b36b 提交
10 months ago
lzy cab494a012 提交
10 months ago
lzy dbd0ce95f8 提交
10 months ago
lzy 924d6e9b8c 提交
10 months ago
lzy 1d5a6ad56e 提交
10 months ago
lzy 6dd4f54ffd 提交
10 months ago
方建军 51536b4301 提交
10 months ago
方建军 cc0e4b6b63 提交
10 months ago
方建军 6099d4e75d 提交
10 months ago
方建军 5a93c224e6 提交
10 months ago
CR7 2d71427403 提交
10 months ago
方建军 63636e38bf 提交
10 months ago
CR7 3cc3778ef8 提交
10 months ago
CR7 2476fe3158 提交
10 months ago
CR7 fe3554e6f3 提交
10 months ago
CR7 1f675f8fe7 提交
10 months ago
CR7 2eef085e09 提交
10 months ago
CR7 84e699930d 提交
10 months ago
CR7 e260078095 提交
10 months ago
方建军 1f4af3300d 提交
10 months ago
CR7 1414e91be9 提交
10 months ago
CR7 69bbcdedb0 提交
10 months ago
CR7 7668da9e2b 提交
10 months ago
CR7 305d551bf7 提交
10 months ago
方建军 d7713c7e88 提交
10 months ago
CR7 a753700ddf 提交
10 months ago
CR7 e30358b7b5 提交
10 months ago
方建军 6a2f36da3b 提交
10 months ago
CR7 d6e9456248 提交
10 months ago
方建军 e3f3a62674 提交
10 months ago
方建军 963b6045c8 提交
10 months ago
方建军 2fc1c0d212 提交
10 months ago
方建军 682dcc3d15 提交
10 months ago
方建军 c13faef2a4 提交
10 months ago
方建军 6520349081 提交
10 months ago
方建军 022612cf96 提交
10 months ago
grey 297d996a06 提交
10 months ago
grey a0df4333b1 提交
10 months ago
grey 18d4c3fb8f 提交
10 months ago
方建军 4de276aba6 Delete 'yami-shop-bean/src/main/java/com/yami/shop/bean/app/param/AddrParam.java'
10 months ago
grey 8e5763ffb6 提交
10 months ago
方建军 3b2bff654b Add FangJianJun_branch
10 months ago
lzy 61e04e3a89 提交
10 months ago
lzy af1283fc07 提交
10 months ago
lzy 317605f376 提交
10 months ago
lzy 9af387b5b0 提交
10 months ago
lzy c9175d98ec 提交
10 months ago
CR7 03cac6de00 提交
10 months ago
CR7 b9198dbd71 提交
10 months ago
CR7 14e3193145 提交
10 months ago
CR7 1825daad59 提交
10 months ago
CR7 c789a1ed46 提交
10 months ago
CR7 baa27f41bc 提交
10 months ago
CR7 c8132ad798 提交
10 months ago
CR7 c5fd53485a 提交
10 months ago
CR7 cc5b9154b6 提交
10 months ago
CR7 07249e3809 提交
10 months ago
CR7 04c811fb77 提交
10 months ago
lzy 177fc39e0e 提交
10 months ago
lzy f5d217ce8d 提交
10 months ago
lzy 47b9999c8a 暂时提交
10 months ago
CR7 e0187fce39 暂时提交
10 months ago
CR7 dcb19e70ac 暂时提交
10 months ago
lzy f8c09d48ce 暂时提交
10 months ago
CR7 7fff432472 暂时提交
10 months ago
p5cp6am9i 4c12a7aa93 Add tangchuanxing_branch
10 months ago
lzy 4f3d66d6d1 暂时提交
10 months ago
lzy 8b763524ea 暂时提交
10 months ago
pyuv9atf8 d1e05100dd Add LiiuZeYu_branch
10 months ago
方建军 ea1f69e4fb Add develop
10 months ago

@ -0,0 +1 @@
undefined

@ -0,0 +1 @@
undefined

@ -0,0 +1 @@
undefined

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

@ -0,0 +1,75 @@
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* Basket
* "tz_basket"MyBatis Plus
* Serializable便使
*
* @author lanhai
*/
@Data
@TableName("tz_basket")
public class Basket implements Serializable {
/**
*
* "tz_basket"
*/
@TableId
private Long basketId;
/**
* ID
* ID
*/
private Long shopId;
/**
* ID
* ID
*/
private Long prodId;
/**
* SkuIDStock Keeping Unit
* SkuID
*/
private Long skuId;
/**
* ID
* ID
* ID
*/
private String userId;
/**
*
*
*/
private Integer basketCount;
/**
*
*
*/
private Date basketDate;
/**
* ID
* ID
*/
private Long discountId;
/**
* 广广
*
*/
private String distributionCardNo;
}

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* `CategoryDto`DTO
* 使
*
* 便
*
* @author lanhai
*/
@Data
public class CategoryDto {
/**
*
*
* `categoryId`
*
*
* `@Schema` API 使 Swagger
* id`required = true`
* 使
*/
@Schema(description = "分类id", required = true)
private Long categoryId;
/**
* `parentId`
*
*
* `id` `parentId`
*
* `@Schema` API id
* 便
*/
@Schema(description = "分类父id", required = true)
private Long parentId;
/**
* `categoryName`
*
*
* 便
*
* `@Schema` API
*
*/
@Schema(description = "分类名称", required = true)
private String categoryName;
/**
* `pic`
* URL
*
*
*
* 使 `@JsonSerialize(using = ImgJsonSerializer.class)`
* `CategoryDto` JSON
* 使 `ImgJsonSerializer` `pic`
* 使
* 使 `@Schema`
* 便使
*/
@Schema(description = "分类图片", required = true)
@JsonSerialize(using = ImgJsonSerializer.class)
private String pic;
}

@ -0,0 +1,75 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import lombok.Data;
import java.io.Serializable;
/**
* `ChooseDiscountItemDto`DTO
*
*
* 便使
*
* @author lanhai
*/
@Data
public class ChooseDiscountItemDto implements Serializable {
/**
*
* `discountItemId`
*
*
*/
private Long discountItemId;
/**
* `discountActivityId`
* ID
*
*
*/
private Long discountActivityId;
/**
* `discountAmount`
*
* 使
*/
private Double discountAmount;
/**
* `isActive`
* `true` `false`
* `true`
* `false`便
*
*/
private boolean isActive;
/**
* `List<Long>`
* ID `applicableProdIds`
*
*
*/
private Object applicableProdIds;
/**
* 使 `remainingUseTimes`
* 使使
* 使3使
* 使使
*/
private Integer remainingUseTimes;
}

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import lombok.Data;
import java.io.Serializable;
/**
* `CouponOrderDto`DTO
* 便
* 使
*
* @author lanhai
*/
@Data
public class CouponOrderDto implements Serializable {
/**
*
* `couponId`
* 使
*
*/
private Long couponId;
/**
* `orderId`
* 使使使
* `orderId` 便
* 使使
*/
private Long orderId;
/**
* `discountAmount`
*
*
*
*/
private Double discountAmount;
/**
* 使 `isUsed` `true` 使`false` 使
* 使使
* 使
* 便
*/
private boolean isUsed;
/**
* 使 `remainingUseTimes`
* 使使
* 使使
* 使使
*/
private Integer remainingUseTimes;
}

@ -0,0 +1,110 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* @author lanhai
*//*
* * Copyright (c) 2018-2999 广 All rights reserved.
* *
* * https://www.mall4j.com/
* *
* *
* *
* *
* */
*
*package com.yami.shop.bean.app.dto;
*
*import io.swagger.v3.oas.annotations.media.Schema;
*import lombok.Data;
*import java.util.List;
*
* /**
* * `DeliveryDto`DTO
* * 便
* * 使
* *
*
* * @author lanhai
* */
*@Data
*
public class DeliveryDto {
*
* /**
* * `companyName`
* *
* * `@Schema` `required = true`
* *
* */
*
@Schema(description = "物流公司名称", required = true)
*
private String companyName;
*
* /**
* * `companyHomeUrl`访
* *
* * 便 `@Schema`
* *
* */
*
@Schema(description = "物流公司官网", required = true)
*
private String companyHomeUrl;
*
* /**
* * `dvyFlowId`
* *
* *
* * `@Schema`
* */
*
@Schema(description = "物流订单号", required = true)
*
private String dvyFlowId;
*
* /**
* * `data` `DeliveryInfoDto`
* *
* * 便
* * `@Schema`
* * 使
* */
*
@Schema(description = "查询出的物流信息", required = true)
*
private List<DeliveryInfoDto> data;
*
}
@Data
public class DeliveryDto {
@Schema(description = "物流公司名称" ,required=true)
private String companyName;
@Schema(description = "物流公司官网" ,required=true)
private String companyHomeUrl;
@Schema(description = "物流订单号" ,required=true)
private String dvyFlowId;
@Schema(description = "查询出的物流信息" ,required=true)
private List<DeliveryInfoDto> data;
}

@ -0,0 +1,68 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* `DeliveryInfoDto`DTO
* 便
*
* @author lanhai
*/
@Data
public class DeliveryInfoDto {
/**
* `context`
*
*
*
*
* `@Schema` `required = true`
* 使
*/
@Schema(description = "详细信息", required = true)
private String context;
/**
* `ftime`
* `yyyy-MM-dd HH:mm:ss`
*
* 便
*
*/
private String ftime;
/**
* `location`线
*
* XXXXXX
*
* `@Schema`
* 便
*/
@Schema(description = "快递所在区域", required = true)
private String location;
/**
* `time` `yyyy-MM-dd HH:mm:ss`
*
*
* 便
* `@Schema`
*
*/
@Schema(description = "物流更新时间", required = true)
private String time;
}

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import lombok.Data;
import java.io.Serializable;
/**
* `DiscountDto`DTO
* 便
* 使
*
* @author lanhai
*/
@Data
public class DiscountDto implements Serializable {
/**
*
* `discountId`200508
*
*
*
*/
private Long discountId;
/**
* `orderId`
* 使使使
* `orderId` 便
* 使使
*/
private Long orderId;
/**
* `discountAmount`
*
*
*
*/
private Double discountAmount;
/**
* 使 `isUsed` `true` 使`false` 使
* 使
* 使
* 便
*/
private boolean isUsed;
/**
* 使 `remainingUseTimes`
* 使使
* 使使
* 使使
*/
private Integer remainingUseTimes;
}

@ -0,0 +1,81 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* `IndexImgDto`DTO
* 使
* 便
* ID
* @author lanhai
*/
@Schema(description = "首页图片对象")
@Data
public class IndexImgDto {
/**
* `imgUrl`访
* 使URL
* `imgUrl`
* `@JsonSerialize(using = ImgJsonSerializer.class)` JSON
* 使使
* `@Schema` Url`required = true`
* 便
*/
@JsonSerialize(using = ImgJsonSerializer.class)
@Schema(description = "图片Url", required = true)
private String imgUrl;
/**
* `seq`
* `seq`
*
* `@Schema`
* 便
*/
@Schema(description = "图片顺序", required = true)
private Integer seq;
/**
* `uploadTime` `Date` `yyyy-MM-dd HH:mm:ss`
* 便
* `@Schema`
*
*/
@Schema(description = "上传时间", required = true)
private Date uploadTime;
/**
* `type`
* `1` `2` `3`
*
* 便
* `@Schema` 便
*/
@Schema(description = "类型", required = true)
private int type;
/**
* `relation` `id` `id`
*
* `relation`
* `@Schema` idID便
*/
@Schema(description = "关联id", required = true)
private Long relation;
}

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* `MyOrderDto`DTO
* 便
* 使便
* @author lanhai
*/
@Data
@Schema(description = "我的订单")
public class MyOrderDto {
/**
* `orderItemDtos` `MyOrderItemDto`
* SKU
* 便
* 退使
* `@Schema` `required = true`
* 便
*/
@Schema(description = "订单项", required = true)
private List<MyOrderItemDto> orderItemDtos;
/**
* `orderNumber`
*
* 退
* `@Schema` 便
*/
@Schema(description = "订单号", required = true)
private String orderNumber;
/**
* `actualTotal` `Double`
* 使
*
* `@Schema`
* 便
*/
@Schema(description = "总价", required = true)
private Double actualTotal;
/**
* `status`
* `0` `1` `2` `3`
*
*
* `@Schema` 便
*/
@Schema(description = "订单状态", required = true)
private Integer status;
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* `MyOrderItemDto`DTO
* 便
* 使
* @author lanhai
*/
@Schema(description = "我的订单-订单项")
@Data
public class MyOrderItemDto {
/**
* `pic`访
* 使URL
* `pic`
* `@JsonSerialize(using = ImgJsonSerializer.class)` JSON
* 使使
* `@Schema` `required = true`
* 便
*/
@Schema(description = "商品图片", required = true)
@JsonSerialize(using = ImgJsonSerializer.class)
private String pic;
/**
* `prodName`P50
*
* `@Schema`
* 便
*/
@Schema(description = "商品名称", required = true)
private String prodName;
/**
* `prodCount`
* 退
* `@Schema` 便
*/
@Schema(description = "商品数量", required = true)
private Integer prodCount;
/**
* `price` `Double`
*
* `@Schema`
* 便
*/
@Schema(description = "商品价格", required = true)
private Double price;
/**
* `skuName`SKUStock Keeping Unit
* SKU8GB + 256GB +
* SKU便
* `@Schema` skuName便
*/
@Schema(description = "skuName", required = true)
private String skuName;
}

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
/**
* `NoticeDto`DTO
* 便
* 使便
*
* @author lanhai
*/
@Schema(description = "公告对象")
@Data
public class NoticeDto {
/**
* `id`IDLong
* ID便
* `@Schema` id
* ID
*
*/
@Schema(description = "公告id")
private Long id;
/**
* `shopId`ID
*
* ID
*/
@Schema(description = "店铺id")
private Long shopId;
/**
* `title`
*
*
*/
@Schema(description = "标题")
private String title;
/**
* `content`
*
*
* 便
*/
@Schema(description = "公告内容")
private String content;
/**
* `publishTime` `Date` `yyyy-MM-dd HH:mm:ss`
*
*
*/
@Schema(description = "公告发布时间")
private Date publishTime;
}

@ -0,0 +1,189 @@
package com.yami.shop.bean.app.param;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
/**
* AddrParamapp使
* 便
*
* @author lanhai
*/
@Schema(description = "地址参数")
public class AddrParam {
/**
*
* @Schema required=true
*/
@Schema(description = "地址ID", required = true)
private Long addrId;
/**
* @NotNull
* 使 @Schema
*/
@NotNull(message = "收货人不能为空")
@Schema(description = "收货人", required = true)
private String receiver;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "地址不能为空")
@Schema(description = "地址", required = true)
private String addr;
/**
* required=false @Schema
*/
@Schema(description = "邮编", required = false)
private String postCode;
/**
*
* 使 @NotNull @Schema
*/
@NotNull(message = "手机不能为空")
@Schema(description = "手机", required = true)
private String mobile;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "省ID不能为空")
@Schema(description = "省ID", required = true)
private Long provinceId;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "城市ID不能为空")
@Schema(description = "城市ID", required = true)
private Long cityId;
/**
*
* @NotNull 使 @Schema
*/
@NotNull(message = "区ID不能为空")
@Schema(description = "区ID", required = true)
private Long areaId;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "省不能为空")
@Schema(description = "省", required = true)
private String province;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "城市不能为空")
@Schema(description = "城市", required = true)
private String city;
/**
*
* @NotNull @Schema
*/
@NotNull(message = "区不能为空")
@Schema(description = "区", required = true)
private String area;
// 以下是各个属性的Getter和Setter方法用于获取和设置对应属性的值
public Long getAddrId() {
return addrId;
}
public void setAddrId(Long addrId) {
this.addrId = addrId;
}
public String getReceiver() {
return receiver;
}
public void setReceiver(String receiver) {
this.receiver = receiver;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getPostCode() {
return postCode;
}
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public Long getProvinceId() {
return provinceId;
}
public void setProvinceId(Long provinceId) {
this.provinceId = provinceId;
}
public Long getCityId() {
return cityId;
}
public void setCityId(Long cityId) {
this.cityId = cityId;
}
public Long getAreaId() {
return areaId;
}
public void setAreaId(Long areaId) {
this.areaId = areaId;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
}

@ -0,0 +1,97 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.param;
import jakarta.validation.constraints.NotNull;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* `ChangeShopCartParam`
* 便
*
* @author LGH
*/
@Data
public class ChangeShopCartParam {
/**
* `basketId`ID
* `basketId`
* ID
*
* `@Schema` API使SwaggerID
* `required = true`
* 便
*/
@Schema(description = "购物车ID", required = true)
private Long basketId;
/**
* `prodId`ID
* `prodId`
* ID
*
* 使 `@NotNull` ID
* ID
* `@Schema` APIID
*/
@NotNull(message = "商品ID不能为空")
@Schema(description = "商品ID", required = true)
private Long prodId;
/**
* `skuId`Stock Keeping UnitSKUID
*
* SKU `skuId`
* 便
* `@NotNull` skuId
* `@Schema` APIskuId便
*/
@NotNull(message = "skuId不能为空")
@Schema(description = "skuId", required = true)
private Long skuId;
/**
* `shopId`ID
*
* `shopId`
*
* `@NotNull` ID
* `@Schema` APIID
*/
@NotNull(message = "店铺ID不能为空")
@Schema(description = "店铺ID", required = true)
private Long shopId;
/**
* `count`
* 12
*
*
* `@NotNull`
* `@Schema` API便
*/
@NotNull(message = "商品个数不能为空")
@Schema(description = "商品个数", required = true)
private Integer count;
/**
* `distributionCardNo`广
* 广
* 广便
* `@Schema` API广
*
*/
@Schema(description = "分销推广人卡号")
private String distributionCardNo;
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.param;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* `LoginParam`
* 便
*
* @author lanhai
*/
@Schema(description = "登陆参数")
public class LoginParam {
/**
* `code` `code` `code`
* `code` `code`
* `@Schema` `code`使 `code`
* `required = true` `code`
*
*/
@Schema(description = "小程序登陆时返回的code(使用code登陆必填)", required = true)
private String code;
/**
* `mobile`
* 便
* `@Schema`
* 使
*/
@Schema(description = "登陆时的用户名(账号密码登陆必填)", required = true)
private String mobile;
/**
* `password` `mobile`
*
* `@Schema`
* 使
*/
@Schema(description = "登陆时的密码(账号密码登陆必填)", required = true)
private String password;
/**
* `getCode`JavaBean访Getter `code`
* 访 `code`
* 访
*/
public String getCode() {
return code;
}
/**
* `setCode` `getCode` Setter `code`
* `code`
* JavaBean便
*/
public void setCode(String code) {
this.code = code;
}
/**
* `getMobile` `mobile` Getter `getCode`
* 使便
*/
public String getMobile() {
return mobile;
}
/**
* `setMobile` `getMobile` Setter `mobile`
*
*
*/
public void setMobile(String mobile) {
this.mobile = mobile;
}
/**
* `getPassword` `password` Getter
* 便使
*
*/
public String getPassword() {
return password;
}
/**
* `setPassword` `password` Setter
*
*/
public void setPassword(String password) {
this.password = password;
}
}

@ -0,0 +1,57 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* `HotSearchDto`DTO
* 便
* 使便
*
* @author lanhai
*/
@Schema(description = "热搜数据")
@Data
public class HotSearchDto implements Serializable {
/**
* `hotSearchId`IDLong
* ID便
* `@Schema` id
* ID
*
*/
@Schema(description = "热搜id")
private Long hotSearchId;
/**
* `title`
*
* `@Schema`
* 便API使Swagger便使
*/
@Schema(description = "标题")
private String title;
/**
* `content`
*
* 便
* `@Schema` `HotSearchDto`
* 便
*/
@Schema(description = "内容")
private String content;
}

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.enums;
/**
* AreaLevelEnum 便
* 使
*
* @author cl
*/
public enum AreaLevelEnum {
/**
* FIRST_LEVEL
* 1
*/
FIRST_LEVEL(1),
/**
* SECOND_LEVEL
* 2便
*/
SECOND_LEVEL(2),
/**
* THIRD_LEVEL
* 3
*/
THIRD_LEVEL(3)
;
/**
*
*
*/
private Integer num;
/**
*
* 便使
*
* @return
*/
public Integer value() {
return num;
}
/**
*
*
*
* @param num
*/
AreaLevelEnum(Integer num) {
this.num = num;
}
/**
* AreaLevelEnum
*
* 便
* null
*
* @param value
* @return AreaLevelEnum null
*/
public static AreaLevelEnum instance(Integer value) {
AreaLevelEnum[] enums = values();
for (AreaLevelEnum statusEnum : enums) {
if (statusEnum.value().equals(value)) {
return statusEnum;
}
}
return null;
}
}

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.enums;
/**
* `MessageStatus`
* 便
* 使
* @author lanhai
*/
public enum MessageStatus {
/**
* `CANCEL` `0`
*
* `CANCEL`
* 便
*/
CANCEL(0),
/**
* `RELEASE` `1`
*
* `RELEASE`
*
*/
RELEASE(1);
/**
* `num`
*
* 使便
*/
private Integer num;
/**
* `value`访 `num`
* `MessageStatus`
* 访
*/
public Integer value() {
return num;
}
/**
* `CANCEL(0)` `RELEASE(1)`
* `num`
* 便 `value`
*/
MessageStatus(Integer num) {
this.num = num;
}
}

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 包声明表明该类所在的包名这里定义在com.yami.shop.bean.event包下
// 按照Java的包结构规范通常用于对相关类进行组织和分类管理方便代码的模块化开发和维护。
package com.yami.shop.bean.event;
// 导入Order类因为在本类中会使用到Order类型的变量通过导入对应的类
// 才能在代码中正确地引用和操作该类型此处表示从com.yami.shop.bean.model包中引入Order类。
import com.yami.shop.bean.model.Order;
// Lombok注解用于自动生成包含所有参数的构造函数
// 这样在创建CancelOrderEvent类的实例时可以方便地通过传入相应参数来初始化对象。
import lombok.AllArgsConstructor;
// Lombok注解用于自动生成类的getter、setter方法以及其他一些常用的方法如toString等
// 减少了手动编写这些重复代码的工作量,提高代码的简洁性和开发效率。
import lombok.Data;
// 类的文档注释,简要描述了该类的作用,即表示取消订单的事件。
// 这种注释有助于其他开发人员快速理解该类在整个业务逻辑中的用途。
/**
*
* @author
*/
// 使用@Data注解让Lombok自动为该类生成相关的方法如getter、setter等。
// 使用@AllArgsConstructor注解让Lombok自动生成包含所有参数的构造函数。
@Data
@AllArgsConstructor
// 定义CancelOrderEvent类用于表示取消订单这一业务事件相关的信息
// 通常在基于事件驱动的架构中,此类可以作为事件对象在不同组件之间传递相关的业务数据。
public class CancelOrderEvent {
// 定义一个私有成员变量order类型为Order用于存储被取消的订单相关的详细信息
// 比如订单编号、下单用户、商品明细、订单金额等内容,方便在事件传递过程中获取和处理订单相关的数据。
private Order order;
}

@ -0,0 +1,62 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.event;
import com.yami.shop.bean.app.dto.ShopCartDto;
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.app.dto.ShopCartOrderDto;
import com.yami.shop.bean.app.dto.ShopCartOrderMergerDto;
import com.yami.shop.bean.app.param.OrderParam;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* `ConfirmOrderEvent`DDD
*
*
* 使
*
* @author LGH
*/
@Data
@AllArgsConstructor
public class ConfirmOrderEvent {
/**
* `shopCartOrderDto`
*
*
* `ShopCartOrderDto` ID
*
* 便
*/
private ShopCartOrderDto shopCartOrderDto;
/**
* `orderParam`
*
*
*
*
*/
private OrderParam orderParam;
/**
* `shopCartItems` `ShopCartItemDto`
* SKU
*
* 便使
*
*/
private List<ShopCartItemDto> shopCartItems;
}

@ -0,0 +1,69 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* Area"tz_area"
* 使MyBatis PlusLombok @Data GetterSetter
*
*
* @author lanhai
*/
@Data
@TableName("tz_area") // 表明该实体类对应的数据库表名称为"tz_area"方便MyBatis Plus进行数据库操作时的表关联映射
public class Area implements Serializable {
private static final long serialVersionUID = -6013320537436191451L;
/**
* @TableId
* @Schema required=true
*/
@TableId
@Schema(description = "地区id", required = true)
private Long areaId;
/**
* @Schema
*
*/
@Schema(description = "地区名称", required = true)
private String areaName;
/**
*
* @Schema 便
*/
@Schema(description = "地区上级id", required = true)
private Long parentId;
/**
* 123
* @Schema
*/
@Schema(description = "地区层级", required = true)
private Integer level;
/**
* @TableField(exist=false)
* 使便
*/
@TableField(exist = false)
private List<Area> areas;
}

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018 - 2999 广 All rights reserved.
* 广20182999
*
* https://www.mall4j.com/
*
*
*
* 使
*
*
*
*/
package com.yami.shop.bean.model;
// 声明该类所属的包名,用于在项目的代码组织结构中对类进行分类管理,方便代码的组织、查找以及不同模块间的引用。
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
// 引入MyBatis Plus相关注解和Lombok的Data注解用于简化代码编写以及实现实体类与数据库表的映射等功能。
import java.io.Serializable;
import java.util.Date;
// 引入Java标准库中的接口和类Serializable接口用于标记该类对象可以被序列化方便在网络传输、存储等场景下进行对象状态的保存和恢复Date类用于处理日期相关的数据。
/**
* @author lanhai
* lanhai
*/
@Data
// Lombok的@Data注解它会自动为类生成常用的方法包括所有成员变量的getter和setter方法、toString方法、equals方法以及hashCode方法等极大地减少了手动编写这些重复代码的工作量使代码更加简洁。
@TableName("tz_attach_file")
// MyBatis Plus的@TableName注解用于将当前的实体类与数据库中的具体表进行映射关联这里表明AttachFile类对应数据库中的"tz_attach_file"表,方便后续进行数据库操作时框架能够准确地知道操作的数据来源和目标表。
public class AttachFile implements Serializable {
// 定义一个名为AttachFile的公共类它实现了Serializable接口意味着该类的实例对象能够进行序列化操作比如可以保存到文件、在网络间传输等场景中使用。
@TableId
// MyBatis Plus的@TableId注解用于标识该成员变量对应的是数据库表中的主键字段在数据库操作如查询、更新、删除等主键是用于唯一标识每条记录的关键属性。
private Long fileId;
// 定义一个名为fileId的私有成员变量类型为Long用于存储文件在系统中的唯一标识符通常对应数据库表中主键列的值通过它可以准确地定位和操作某一条具体的文件相关记录。
/**
*
* filePath
*/
private String filePath;
// 定义一个名为filePath的私有成员变量类型为String用于记录文件的存储路径。
/**
*
* fileType"txt""jpg""pdf"PDF便
*/
private String fileType;
// 定义一个名为fileType的私有成员变量类型为String用于存储文件的类型标识。
/**
*
* fileSize使
*/
private Integer fileSize;
// 定义一个名为fileSize的私有成员变量类型为Integer用于存储文件的大小数值通常以字节为单位来衡量。
/**
*
* uploadTime
*/
private Date uploadTime;
// 定义一个名为uploadTime的私有成员变量类型为Date用于保存文件上传的时间信息。
/**
* id
* fileJoinId
*/
private Long fileJoinId;
// 定义一个名为fileJoinId的私有成员变量类型为Long用于记录与文件相关联的其他表的主键值。
/**
* 1 @see FileJoinType
* fileJoinType1FileJoinType@see
*/
private Integer fileJoinType;
// 定义一个名为fileJoinType的私有成员变量类型为Integer用于标识文件关联表的类型信息。
}

@ -0,0 +1,75 @@
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* Basket
* "tz_basket"MyBatis Plus
* Serializable便使
*
* @author lanhai
*/
@Data
@TableName("tz_basket")
public class Basket implements Serializable {
/**
*
* "tz_basket"
*/
@TableId
private Long basketId;
/**
* ID
* ID
*/
private Long shopId;
/**
* ID
* ID
*/
private Long prodId;
/**
* SkuIDStock Keeping Unit
* SkuID
*/
private Long skuId;
/**
* ID
* ID
* ID
*/
private String userId;
/**
*
*
*/
private Integer basketCount;
/**
*
*
*/
private Date basketDate;
/**
* ID
* ID
*/
private Long discountId;
/**
* 广广
*
*/
private String distributionCardNo;
}

@ -0,0 +1,96 @@
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* Brand"tz_brand"
* MyBatis Plus@TableName
* Serializable便
*
* @author lanhai
*/
@Data
@TableName("tz_brand")
public class Brand implements Serializable {
/**
*
* "tz_brand"
*
*/
@TableId
private Long brandId;
/**
*
*
*/
private String brandName;
/**
*
* URL
* logo
*/
private String brandPic;
/**
* ID
* ID
* 便
*/
private String userId;
/**
*
* 沿便
*/
private String memo;
/**
*
* 便
*/
private Integer seq;
/**
* 10线
*
* 0使线
*/
private Integer status;
/**
*
*
*/
private String brief;
/**
*
*
*/
private Date recTime;
/**
*
*
*/
private Date updateTime;
/**
*
* 便
*/
private String firstChar;
/**
*
* 使
*/
private String content;
}

@ -0,0 +1,170 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 声明该类所在的包名按照Java的包结构组织方式将相关的类放在同一个包下方便管理和区分不同功能模块的代码
// 这里表示该类属于com.yami.shop.bean.model包。
package com.yami.shop.bean.model;
// 导入MyBatis Plus的注解TableField用于定义实体类中字段与数据库表字段之间的一些额外映射关系
// 比如指定字段是否在数据库表中有对应列等情况。
import com.baomidou.mybatisplus.annotation.TableField;
// 导入MyBatis Plus的注解TableId用于标记实体类中的主键字段使得MyBatis Plus能正确识别与数据库表主键的对应关系。
import com.baomidou.mybatisplus.annotation.TableId;
// 导入MyBatis Plus的注解TableName用于将实体类与数据库中的具体表进行映射表明该实体类对应哪个数据库表。
import com.baomidou.myatisplus.annotation.TableName;
// 导入Jackson的注解JsonSerialize用于指定在将对象转换为JSON格式时对特定字段使用自定义的序列化方式。
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
// 导入自定义的ImgJsonSerializer类它应该是用于处理图片相关字段序列化的类
// 在这里配合@JsonSerialize注解来对特定的图片字段进行定制化的JSON序列化操作。
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
// 导入Lombok的Data注解使用该注解后Lombok会自动为类生成常用的方法比如getter、setter、toString等方法
// 减少了手动编写这些重复代码的工作量,提高代码的简洁性和开发效率。
import lombok.Data;
// 导入Java的相关接口和类用于实现对象的序列化功能Serializable接口以及处理日期相关操作Date类
// 还有用于操作列表数据结构List接口使得该类能更好地满足常见的业务需求比如数据存储、传输等场景。
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* lanhai
* 便
* @author lanhai
*/
// 使用@Data注解让Lombok自动生成类的getter、setter等常用方法简化代码编写。
// 使用@TableName注解将该Category类与数据库中的"tz_category"表进行映射关联,意味着该类的实例可以与数据库表中的记录相互转换操作。
@Data
@TableName("tz_category")
public class Category implements Serializable {
/**
* ID
* "tz_category"
* ID
*/
@TableId
private Long categoryId;
/**
* id
* id
*
*/
private Long shopId;
/**
* ID
* 0L
* 便
*/
private Long parentId = 0L;
/**
*
*
*/
private String categoryName;
/**
*
* URL
* 使
*/
private String icon;
/**
* @JsonSerialize使ImgJsonSerializer
* JSON
* 便
*/
@JsonSerialize(using = ImgJsonSerializer.class)
private String pic;
/**
*
* 便
*
*/
private Integer seq;
/**
* 10线
* 使
* 0使线
*/
private Integer status;
/**
*
*
*/
private Date recTime;
/**
*
* 12便
*/
private Integer grade;
/**
*
*
*
*/
private Date updateTime;
/**
* id使@TableField(exist=false)"tz_category"
* ID便
*/
@TableField(exist=false)
private List<Long> brandIds;
/**
* id使@TableField(exist=false)
* ID
*
*/
@TableField(exist=false)
private List<Long> attributeIds;
/**
* @TableField(exist=false)
* ID便
* 使
*/
@TableField(exist=false)
private List<Brand> brands;
/**
* @TableField(exist=false)
* 便
*
*/
@TableField(exist=false)
private List<ProdProp> prodProps;
/**
* 使@TableField(exist=false)
* 便
*
*/
@TableField(exist=false)
private List<Product> products;
/**
* @TableField(exist=false)
* 便
*
*/
@TableField(exist=false)
private List<Category> categories;
}

@ -0,0 +1,59 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.dto;
import lombok.Data;
import java.io.Serializable;
/**
* DTO
*
* 便
* 使
* 便
*
* @author lanhai
*/
@Data
public class ChooseDiscountItemDto implements Serializable {
// 此处可根据实际业务需求添加相应的属性及对应的注释,以下为示例,可按需调整替换
/**
*
* ID
* 便
*/
private Long discountItemId;
/**
* ID
* ID
* 便
*/
private Long discountActivityId;
/**
*
*
*
*/
private Double discountAmount;
/**
* truefalse
* true
* false
* 便
*/
private boolean isActive;
}

@ -0,0 +1,53 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
/**
* `CategoryProp`
* 便
*
*
* @author lanhai
*/
@Data
@TableName("tz_category_prop")
public class CategoryProp implements Serializable {
/**
* `id`
* `id`
*
*
*/
@TableId
private Long id;
/**
* `categoryId`ID
* ID
* ID
*
*/
private Long categoryId;
/**
* `propId`ID `tz_prod_prop` `prop_id`
*
* `propId` ID
*
* 便
*/
private Long propId;
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* `Delivery`
* 便
*
* @author lanhai
*/
@Data
@TableName("tz_delivery")
public class Delivery implements Serializable {
/**
* `dvyId`
* `dvyId`
* ID
*
*/
@TableId
private Long dvyId;
/**
* `dvyName`
*
* 便
*/
private String dvyName;
/**
* `companyHomeUrl`访
*
* 使便访
*/
private String companyHomeUrl;
/**
* `recTime`
* `Date` `yyyy-MM-dd HH:mm:ss`
*
*/
private Date recTime;
/**
* `modifyTime` `Date`
*
* 便
*/
private Date modifyTime;
/**
* `queryUrl`
*
*
*/
private String queryUrl;
}

@ -0,0 +1,68 @@
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* HotSearch"tz_hot_search"
* MyBatis Plus@TableName
* Serializable便
*
* @author lanhai
*/
@Data
@TableName("tz_hot_search")
public class HotSearch implements Serializable {
/**
*
* "tz_hot_search"
*
*/
@TableId
private Long hotSearchId;
/**
* id
* id
*
*/
private Long shopId;
/**
*
* 便
*/
private String title;
/**
*
*
*/
private String content;
/**
*
*
* @DateTimeFormat"yyyy-MM-dd HH:mm:ss"便
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date recDate;
/**
*
* 使
*/
private Integer seq;
/**
* 10线
*
* 0使线
*/
private Integer status;
}

@ -0,0 +1,129 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* `IndexImg`
* 便
*
* @author lanhai
*/
@Data
@TableName("tz_index_img")
public class IndexImg implements Serializable {
private static final long serialVersionUID = -3468251351681518798L;
/**
* `imgId`
* `imgId`
* ID
*
*/
@TableId
private Long imgId;
/**
* `shopId`ID
*
* ID
*/
private Long shopId;
/**
* `imgUrl`访
* 使URL
*
*/
private String imgUrl;
/**
* `des`
*
*
*/
private String des;
/**
* `title`
*
*
*/
private String title;
/**
* `link`
*
*
*/
private String link;
/**
* `status` `0`
* `1` 便
*
*/
private Integer status;
/**
* `seq`
* `seq`
*
*/
private Integer seq;
/**
* `uploadTime` `Date` `yyyy-MM-dd HH:mm:ss`
*
*/
private Date uploadTime;
/**
* `type` `1` `2`
* `3`
* 便
*/
private int type;
/**
* `relation` `id` `id`
*
* 便
*/
private Long relation;
/**
* `pic` `@TableField(exist = false)`
* 使 `@JsonSerialize(using = ImgJsonSerializer.class)`
* JSON使
* 使便
*/
@TableField(exist = false)
@JsonSerialize(using = ImgJsonSerializer.class)
private String pic;
/**
* `prodName` `@TableField(exist = false)`
*
*
*/
@TableField(exist = false)
private String prodName;
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.app.param;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* `LoginParam`
* 便
*
* @author lanhai
*/
@Schema(description = "登陆参数")
public class LoginParam {
/**
* `code` `code` `code`
* `code` `code`
* `@Schema` `code`使 `code`
* `required = true` `code`
*
*/
@Schema(description = "小程序登陆时返回的code(使用code登陆必填)", required = true)
private String code;
/**
* `mobile`
* 便
* `@Schema`
* 使
*/
@Schema(description = "登陆时的用户名(账号密码登陆必填)", required = true)
private String mobile;
/**
* `password` `mobile`
*
* `@Schema`
* 使
*/
@Schema(description = "登陆时的密码(账号密码登陆必填)", required = true)
private String password;
/**
* `getCode`JavaBean访Getter `code`
* 访 `code`
* 访
*/
public String getCode() {
return code;
}
/**
* `setCode` `getCode` Setter `code`
* `code`
* JavaBean便
*/
public void setCode(String code) {
this.code = code;
}
/**
* `getMobile` `mobile` Getter `getCode`
* 使便
*/
public String getMobile() {
return mobile;
}
/**
* `setMobile` `getMobile` Setter `mobile`
*
*
*/
public void setMobile(String mobile) {
this.mobile = mobile;
}
/**
* `getPassword` `password` Getter
* 便使
*
*/
public String getPassword() {
return password;
}
/**
* `setPassword` `password` Setter
*
*/
public void setPassword(String password) {
this.password = password;
}
}

@ -0,0 +1,76 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
*
*
* @author hzm
* @date 2019-04-18 21:21:40
*/
@Data
@TableName("tz_notice")
public class Notice implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId
private Long id;
/**
* id
*/
private Long shopId;
/**
*
*/
private String title;
/**
*
*/
private String content;
/**
* (1: 0:)
*/
private Integer status;
/**
* 1: 0
*/
private Integer isTop;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date publishTime;
/**
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}

@ -0,0 +1,217 @@
package com.yami.shop.bean.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author lanhai
* @description Order
* 便便
*/
@Data
// 使用@TableName注解指定该实体类对应的数据库表名为"tz_order"用于MyBatis Plus框架建立实体与表的映射关系
@TableName("tz_order")
public class Order implements Serializable {
private static final long serialVersionUID = 6222259729062826852L;
/**
* @description ID使@TableId"tz_order"
*/
@TableId
private Long orderId;
/**
* @description idid
*/
private Long shopId;
/**
* @description
*
*/
private String prodName;
/**
* @description ID
* 便
*/
private String userId;
/**
* @description
*
*/
private String orderNumber;
/**
* @description
*/
private Double total;
/**
* @description
*
*/
private Double actualTotal;
/**
* @description 1 2
* 便退
*/
private Integer payType;
/**
* @description
* 便
*/
private String remarks;
/**
* @description
* - -1
* - 0
* - 1
* - 2
* - 3
*
*/
private Integer status;
/**
* @description
* 便
*/
private String dvyType;
/**
* @description ID
*
*/
private Long dvyId;
/**
* @description
* 便
*/
private String dvyFlowId;
/**
* @description
*
*/
private Double freightAmount;
/**
* @description Id
*
*/
private Long addrOrderId;
/**
* @description 便
*
*/
private Integer productNums;
/**
* @description 使@DateTimeFormat"yyyy-MM-dd HH:mm:ss"
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* @description 使@DateTimeFormat
* 便
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/**
* @description 使@DateTimeFormat
* 退
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date payTime;
/**
* @description 使@DateTimeFormat
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date dvyTime;
/**
* @description 使@DateTimeFormat
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date finallyTime;
/**
* @description 使@DateTimeFormat
*
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date cancelTime;
/**
* @description 10
* 退
*/
private Integer isPayed;
/**
* @description
* - 0
* - 1
* - 2
* 便
*/
private Integer deleteStatus;
/**
* @description 退退
* - 0退
* - 1退退
* - 2退便退
*/
private Integer refundSts;
/**
* @description
*
*/
private Double reduceAmount;
/**
* @description 使@TableField(exist = false)"tz_order"
*
* 便
*/
@TableField(exist = false)
private String shopName;
/**
* @description 使@TableField(exist = false)
* OrderItemID
* 便
*/
@TableField(exist = false)
private List<OrderItem> orderItems;
/**
* @description 使@TableField(exist = false)
* UserAddrOrder
* 便
*/
@TableField(exist = false)
private UserAddrOrder userAddrOrder;
}

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.order;
/**
* `ConfirmOrderOrder`
*
*
*
* @author LGH
*/
public interface ConfirmOrderOrder {
/**
* `DEFAULT`0
*
*
*
*/
int DEFAULT = 0;
/**
* `DISCOUNT`100 `DEFAULT`
*
* `DEFAULT`
*
*/
int DISCOUNT = 100;
/**
* `COUPON`200 `DISCOUNT`
* 使
* `DISCOUNT`
* 使
*/
int COUPON = 200;
/**
* `DISTRIBUTION`300 `COUPON`
*
* `COUPON`
*
*/
int DISTRIBUTION = 300;
}

@ -0,0 +1,129 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.bean.param;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
/**
* `DeliveryOrderParam`
* 便
*
* @author lanhai
*/
public class DeliveryOrderParam {
/**
* `orderNumber`
*
*
*
*
* 使 `@NotBlank`
*
* `@Schema` API 使 Swagger
* `required = true`
*
*/
@NotBlank(message = "订单号不能为空")
@Schema(description = "订单号", required = true)
private String orderNumber;
/**
* `dvyId` ID `Long`
* ID
* ID
* `dvyId`
*
* `@NotBlank` id
* ID `@Schema` API
* 便
*
*/
@NotBlank(message = "快递公司id不能为空")
@Schema(description = "快递公司", required = true)
private Long dvyId;
/**
* `dvyFlowId`
*
*
*
* 使 `@NotBlank`
* `@Schema` API
*
*/
@NotBlank(message = "物流单号不能为空")
@Schema(description = "物流单号", required = true)
private String dvyFlowId;
/**
* `getDvyId` JavaBean 访Getter `dvyId`
* `DeliveryOrderParam` ID
* 访
* 访使
*/
public Long getDvyId() {
return dvyId;
}
/**
* `setDvyId` `getDvyId` Setter `dvyId`
* ID
* `Long` `dvyId`
* ID ID
* ID
*/
public void setDvyId(Long dvyId) {
this.dvyId = dvyId;
}
/**
* `getDvyFlowId` Getter `dvyFlowId`
*
* JavaBean 使访便使
*/
public String getDvyFlowId() {
return dvyFlowId;
}
/**
* `setDvyFlowId` `getDvyFlowId` Setter `dvyFlowId`
*
* `DeliveryOrderParam`
*
*
*/
public void setDvyFlowId(String dvyFlowId) {
this.dvyFlowId = dvyFlowId;
}
/**
* `getOrderNumber` `orderNumber` Getter Getter
*
*
* JavaBean 使访
*/
public String getOrderNumber() {
return orderNumber;
}
/**
* `setOrderNumber` `getOrderNumber` Setter `orderNumber`
*
*
*
*/
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.bean;
// 导入lombok的Data注解使用该注解可以自动生成类的一些常规方法比如getter、setter、toString等方法简化代码编写
import lombok.Data;
/**
*
* 访ID访
* 便
* @author LGH
*/
@Data
// 定义名为AliDaYu的公共类用于封装阿里大鱼配置相关的属性
public class AliDaYu {
// 以下是类的私有属性,用于存储具体的配置信息
// 用于存储阿里大鱼的访问密钥ID通过该ID来标识访问阿里大鱼服务的身份凭证之一
private String accessKeyId;
// 用于存储阿里大鱼的访问密钥密码与访问密钥ID配合使用用于进行安全验证等操作确保对阿里大鱼服务的合法访问
private String accessKeySecret;
// 用于存储阿里大鱼的签名名称,在涉及到一些需要签名验证的业务场景中会用到该名称
private String signName;
}

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.bean;
// 引入Lombok的Data注解通过该注解编译器会自动帮我们生成类的常用方法
// 比如各属性的Getter、Setter方法以及toString、equals、hashCode方法等简化代码编写
import lombok.Data;
/**
* ImgUpload
* 便使
*
* @author lgh
*/
@Data
public class ImgUpload {
/**
*
* 便访
*/
private String imagePath;
/**
*
* 1imagePath
* 2使
*/
private Integer uploadType;
/**
* 访URL访
* resourceUrl访URL访
*/
private String resourceUrl;
}

@ -0,0 +1,133 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.config;
// 导入相关的自定义异常类、响应相关的枚举、响应实体类等
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.response.ResponseEnum;
import com.yami.shop.common.response.ServerResponseEntity;
// 导入日志相关的Lombok注解用于简化日志记录代码
import lombok.extern.slf4j.Slf4j;
// 导入Spring的HTTP状态码相关枚举类
import org.springframework.http.HttpStatus;
// 导入Spring用于构建HTTP响应实体的类
import org.springframework.http.ResponseEntity;
// 导入Spring用于标记控制器类的注解此处结合异常处理相关特性使用
import org.springframework.stereotype.Controller;
// 导入Spring在数据校验场景下的异常类以及表示字段错误的类
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
// 导入Spring用于定义异常处理方法的注解以及标记全局异常处理类的注解
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
// 导入Spring用于处理资源未找到异常的类
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.util.ArrayList;
import java.util.List;
/**
*
*
* @author LGH
*/
@Slf4j
@Controller
@RestControllerAdvice
public class DefaultExceptionHandlerConfig {
/**
* MethodArgumentNotValidExceptionBindException
* Spring
*
*
* @param e MethodArgumentNotValidExceptionBindException
* @return ResponseEntity<ServerResponseEntity<List<String>>> HTTP
*
*/
@ExceptionHandler({ MethodArgumentNotValidException.class, BindException.class })
public ResponseEntity<ServerResponseEntity<List<String>>> methodArgumentNotValidExceptionHandler(Exception e) {
// 记录异常信息,方便后续查看出现异常的具体情况,用于调试和问题排查
log.error("methodArgumentNotValidExceptionHandler", e);
List<FieldError> fieldErrors = null;
// 判断异常类型是否是MethodArgumentNotValidException如果是则获取对应的字段错误列表
if (e instanceof MethodArgumentNotValidException) {
fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
}
// 判断异常类型是否是BindException如果是则获取对应的字段错误列表
if (e instanceof BindException) {
fieldErrors = ((BindException) e).getBindingResult().getFieldErrors();
}
// 如果没有获取到字段错误列表(可能出现不符合预期的异常情况等)
if (fieldErrors == null) {
// 返回一个包含特定失败响应枚举(表示参数校验不通过)的响应实体,没有具体字段错误信息
return ResponseEntity.status(HttpStatus.OK)
.body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID));
}
// 用于存放每个字段的错误信息字符串(格式为:字段名:默认错误消息)
List<String> defaultMessages = new ArrayList<>(fieldErrors.size());
// 遍历字段错误列表拼接每个字段的错误信息字符串并添加到defaultMessages列表中
for (FieldError fieldError : fieldErrors) {
defaultMessages.add(fieldError.getField() + ":" + fieldError.getDefaultMessage());
}
// 返回一个包含具体字段错误信息列表的失败响应实体,使用参数校验不通过的响应枚举标识
return ResponseEntity.status(HttpStatus.OK)
.body(ServerResponseEntity.fail(ResponseEnum.METHOD_ARGUMENT_NOT_VALID, defaultMessages));
}
/**
* YamiShopBindException
* YamiShopBindException
*
*
* @param e YamiShopBindException
* @return ResponseEntity<ServerResponseEntity<?>> HTTP
*
*/
@ExceptionHandler(YamiShopBindException.class)
public ResponseEntity<ServerResponseEntity<?>> unauthorizedExceptionHandler(YamiShopBindException e){
// 记录异常信息,方便后续排查问题,了解异常出现的具体情况
log.error("mall4jExceptionHandler", e);
ServerResponseEntity<?> serverResponseEntity = e.getServerResponseEntity();
// 如果异常中已经封装好了响应实体(在抛出异常时可能已经构建好了特定的响应内容)
if (serverResponseEntity!=null) {
// 直接将其作为响应内容返回给客户端设置状态码为OK
return ResponseEntity.status(HttpStatus.OK).body(serverResponseEntity);
}
// 失败返回消息,状态码固定为直接显示消息的状态码,根据异常的错误码和错误消息构建失败响应实体并返回
return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(e.getCode(),e.getMessage()));
}
/**
* Exception
*
*
*
* @param e Exception
* @return ResponseEntity<ServerResponseEntity<Object>> HTTP
*
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ServerResponseEntity<Object>> exceptionHandler(Exception e){
// 判断异常是否是资源未找到异常类型
if (e instanceof NoResourceFoundException) {
// 如果是则返回一个包含异常消息的失败响应实体告知客户端资源未找到相关错误信息状态码设为OK
return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.showFailMsg(e.getMessage()));
}
// 记录异常信息,方便后续排查问题,知道出现异常的具体情况
log.error("exceptionHandler", e);
// 返回一个使用默认的异常响应枚举构建的失败响应实体,告知客户端出现了未预期的通用异常情况
return ResponseEntity.status(HttpStatus.OK).body(ServerResponseEntity.fail(ResponseEnum.EXCEPTION));
}
}

@ -0,0 +1,112 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.config;
// 导入相关的自定义类,可能用于存储图片上传相关的配置、枚举等信息
import com.yami.shop.common.bean.ImgUpload;
import com.yami.shop.common.enums.QiniuZone;
// 导入Spring的注解相关类用于实现依赖注入和配置相关功能
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// 导入七牛云相关的类,用于与七牛云存储服务进行交互,涉及区域、上传管理、认证等功能
import com.qiniu.common.Zone;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.util.Auth;
import com.yami.shop.common.bean.Qiniu;
import java.util.Objects;
/**
*
*
* 便
* @author lgh
*/
@Configuration
public class FileUploadConfig {
// 通过Spring的依赖注入获取Qiniu类型的配置对象该对象应该包含了七牛云相关的配置信息如密钥、机房区域等
@Autowired
private Qiniu qiniu;
/**
* com.qiniu.storage.Configuration
* QiniuQiniuZoneZone
* 使
*
* @return com.qiniu.storage.Configuration
*/
@Bean
public com.qiniu.storage.Configuration qiniuConfig() {
Zone zone = null;
// 判断配置中的机房区域是否为华北区如果是则设置对应的七牛云机房区域为华北区Zone.huabei()
if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_BEI)) {
zone = Zone.huabei();
}
// 判断是否为华东区若是则设置对应的七牛云机房区域为华东区Zone.huadong()
else if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_DONG)) {
zone = Zone.huadong();
}
// 判断是否为华南区若是则设置对应的七牛云机房区域为华南区Zone.huanan()
else if (Objects.equals(qiniu.getZone(), QiniuZone.HUA_NAN)) {
zone = Zone.huanan();
}
// 判断是否为北美区若是则设置对应的七牛云机房区域为北美区Zone.beimei()
else if (Objects.equals(qiniu.getZone(), QiniuZone.BEI_MEI)) {
zone = Zone.beimei();
}
// 判断是否为新加坡区若是则设置对应的七牛云机房区域为新加坡区Zone.xinjiapo()
else if (Objects.equals(qiniu.getZone(), QiniuZone.XIN_JIA_PO)) {
zone = Zone.xinjiapo();
}
// 使用确定好的机房区域zone构建七牛云存储配置实例并返回
return new com.qiniu.storage.Configuration(zone);
}
/**
* UploadManager
* qiniuConfigcom.qiniu.storage.Configuration
*
*
* @return UploadManager
*/
@Bean
public UploadManager uploadManager() {
return new UploadManager(qiniuConfig());
}
/**
* Auth
* Qiniu访accessKeysecretKey
*
*
* @return Auth 访
*/
@Bean
public Auth auth() {
return Auth.create(qiniu.getAccessKey(), qiniu.getSecretKey());
}
/**
* BucketManager
* authAuthqiniuConfig
*
*
* @return BucketManager
*/
@Bean
public BucketManager bucketManager() {
return new BucketManager(auth(), qiniuConfig());
}
}

@ -0,0 +1,26 @@
package com.yami.shop.common.constants;
/**
* Constant
*
* 便
*
*
* @author TRACK
*/
public class Constant {
/**
* PERIOD
* 使
* 使
*/
public static final String PERIOD = ".";
/**
* COMMA
* CSV
* 便使
*/
public static final String COMMA = ",";
}

@ -0,0 +1,131 @@
package com.yami.shop.common.handler;
// 导入 Hutool 工具库中处理字符集相关的工具类,用于设置响应的字符编码
import cn.hutool.core.util.CharsetUtil;
// 导入 Jackson 库中用于将对象转换为 JSON 字符串以及反序列化等操作的核心类
import com.fasterxml.jackson.databind.ObjectMapper;
// 导入自定义的业务异常类,可能在项目中用于处理特定业务逻辑出错的情况
import com.yami.shop.common.exception.YamiShopBindException;
// 导入自定义的用于封装服务器响应信息的实体类,包含响应状态码、消息、数据等内容
import com.yami.shop.common.response.ServerResponseEntity;
// 导入 Slf4j 框架的日志记录相关类,用于创建日志记录器来记录不同情况的日志信息
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 导入 Spring 框架用于实现依赖注入的注解,表明某个属性需要由 Spring 容器进行注入
import org.springframework.beans.factory.annotation.Autowired;
// 导入 Spring 框架中定义媒体类型的枚举类,用于设置响应的内容类型为 JSON 格式
import org.springframework.http.MediaType;
// 导入 Spring 框架用于将类标记为组件的注解,表明该类是一个 Spring 管理的组件,可被自动扫描并注入到其他需要的地方
import org.springframework.stereotype.Component;
// 导入 Spring 框架中用于获取请求上下文相关信息的类和接口
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
// 导入 Servlet 相关的用于操作 HTTP 响应的类,用于设置响应的各种属性以及向客户端输出内容
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Objects;
/**
* HttpHandler Web
* JSON HTTP
*
*
* @author
* @date 2022/3/28 14:15
*/
@Component
public class HttpHandler {
// 创建一个日志记录器,用于记录该类中不同操作阶段的日志信息,方便后续进行问题排查和调试
private static final Logger logger = LoggerFactory.getLogger(HttpHandler.class);
// 通过 Spring 的依赖注入机制,注入一个 ObjectMapper 对象,用于将对象转换为 JSON 字符串以便输出到客户端
@Autowired
private ObjectMapper objectMapper;
/**
* ServerResponseEntity JSON Web HTTP
* null
* HTTP
* I/O YamiShopBindException
*
* @param serverResponseEntity
* @param <T>
*/
public <T> void printServerResponseToWeb(ServerResponseEntity<T> serverResponseEntity) {
// 如果传入的服务器响应实体为 null记录日志提示信息并直接返回不进行后续操作
if (serverResponseEntity == null) {
logger.info("print obj is null");
return;
}
// 从 Spring 的请求上下文中获取 ServletRequestAttributes 对象,它包含了与当前请求相关的信息,如请求和响应对象等
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
// 如果获取到的 ServletRequestAttributes 对象为 null说明无法获取到请求上下文相关信息记录错误日志并返回无法进行响应输出操作
if (requestAttributes == null) {
logger.error("requestAttributes is null, can not print to web");
return;
}
// 从 ServletRequestAttributes 对象中获取 HttpServletResponse 对象,用于后续设置响应属性和向客户端输出内容
HttpServletResponse response = requestAttributes.getResponse();
// 如果获取到的 HttpServletResponse 对象为 null说明无法获取到有效的 HTTP 响应对象,记录错误日志并返回,无法进行响应输出操作
if (response == null) {
logger.error("httpServletResponse is null, can not print to web");
return;
}
// 记录响应的错误消息(这里假设 getMsg 方法获取的是错误相关信息,实际情况可能根据 ServerResponseEntity 的具体实现而定)到日志中,方便排查问题
logger.error("response error: " + serverResponseEntity.getMsg());
// 设置 HTTP 响应的字符编码为 UTF-8确保输出的内容能够正确地被客户端解析尤其是包含中文等多字节字符的情况
response.setCharacterEncoding(CharsetUtil.UTF_8);
// 设置 HTTP 响应的内容类型为 application/json表明响应的内容是 JSON 格式的数据,让客户端能够正确识别并解析
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
// 用于向 HTTP 响应中写入字符数据的对象,初始化为 null后续通过获取响应的输出流来实例化
PrintWriter printWriter = null;
try {
// 获取 HttpServletResponse 的输出流对象,用于向客户端写入响应内容(这里是将服务器响应实体转换后的 JSON 字符串写入)
printWriter = response.getWriter();
// 使用注入的 ObjectMapper 对象将服务器响应实体转换为 JSON 字符串,并写入到 HTTP 响应的输出流中,从而输出到客户端
printWriter.write(objectMapper.writeValueAsString(serverResponseEntity));
} catch (IOException e) {
// 如果在写入过程中出现 I/O 异常抛出自定义的业务异常YamiShopBindException并将原始的 I/O 异常作为原因传递,方便上层进行统一的异常处理和日志记录
throw new YamiShopBindException("io 异常", e);
}
}
/**
* YamiShopBindException JSON Web HTTP
* null
* ServerResponseEntity printServerResponseToWeb
* printServerResponseToWeb
*
* @param yamiShopBindException YamiShopBindException
* @param <T>
*/
public <T> void printServerResponseToWeb(YamiShopBindException yamiShopBindException) {
// 如果传入的 YamiShopBindException 异常对象为 null记录日志提示信息并直接返回不进行后续操作
if (yamiShopBindException == null) {
logger.info("print obj is null");
return;
}
// 判断异常对象中是否包含了服务器响应实体ServerResponseEntity如果包含则直接调用 printServerResponseToWeb 方法输出该实体内容到客户端
if (Objects.nonNull(yamiShopBindException.getServerResponseEntity())) {
printServerResponseToWeb(yamiShopBindException.getServerResponseEntity());
return;
}
// 如果异常对象中没有包含服务器响应实体,则创建一个新的 ServerResponseEntity 对象,用于封装异常中的错误码和错误消息等信息
ServerResponseEntity<T> serverResponseEntity = new ServerResponseEntity<>();
serverResponseEntity.setCode(yamiShopBindException.getCode());
serverResponseEntity.setMsg(yamiShopBindException.getMessage());
// 调用 printServerResponseToWeb 方法将构建好的服务器响应实体输出到客户端,完成响应信息的输出操作
printServerResponseToWeb(serverResponseEntity);
}
}

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.serializer.json;
// 导入Hutool工具库中用于字符串操作的工具类例如判断字符串是否为空、处理字符串拼接等操作
import cn.hutool.core.util.StrUtil;
// 导入Jackson库中用于自定义JSON序列化相关的核心类用于定义如何将Java对象序列化为JSON格式
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
// 导入自定义的类Qiniu类可能包含了七牛云相关的配置信息如资源访问地址等
import com.yami.shop.common.bean.Qiniu;
// 导入自定义的图片上传工具类,可能用于获取图片上传相关的配置信息,比如上传类型、资源地址等
import com.yami.shop.common.util.ImgUploadUtil;
// 导入Spring框架用于实现依赖注入的注解以及将类标记为组件的注解表明该类是受Spring管理的组件可被自动注入到需要的地方
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* ImgJsonSerializerJSONJSON
* httphttps访
* JSONJSON
*
* @author lanhai
*/
@Component
public class ImgJsonSerializer extends JsonSerializer<String> {
// 通过Spring的依赖注入机制注入一个Qiniu对象该对象可能包含了七牛云存储相关的配置信息比如七牛云资源的访问地址等
@Autowired
private Qiniu qiniu;
// 注入一个ImgUploadUtil对象用于获取图片上传相关的配置信息例如图片上传类型等辅助对图片路径进行处理
@Autowired
private ImgUploadUtil imgUploadUtil;
/**
* JacksonserializeJSON
*
* @param value
* @param gen JacksonJsonGeneratorJSONJSON
* @param serializers JacksonSerializerProvider
* @throws IOException JsonGeneratorI/O
*/
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// 判断传入的要序列化的字符串是否为空空白字符串包括null、空字符串以及只包含空格等不可见字符的字符串
if (StrUtil.isBlank(value)) {
// 如果为空则向JsonGenerator写入一个空字符串保持JSON格式的一致性然后直接返回不再进行后续处理
gen.writeString(StrUtil.EMPTY);
return;
}
// 将传入的字符串按照逗号进行分割,得到一个字符串数组,假设这里的字符串表示多个图片路径,以逗号分隔开,每个元素就是一个单独的图片路径
String[] imgs = value.split(StrUtil.COMMA);
// 创建一个可变的字符串构建器,用于拼接处理后的图片路径,方便后续构建最终的序列化字符串
StringBuilder sb = new StringBuilder();
// 用于存储资源访问地址,根据不同的上传类型来确定具体的值,后续会将其添加到图片路径前面(如果图片路径本身不符合要求的话)
String resourceUrl = "";
// 定义一个正则表达式字符串,用于匹配以"http"或"https"开头的字符串,目的是判断图片路径是否已经是完整的网络访问地址形式
String rule = "^((http[s]{0,1})://)";
// 使用定义好的正则表达式创建一个Pattern对象用于后续进行正则匹配操作
Pattern pattern = Pattern.compile(rule);
// 根据ImgUploadUtil对象获取的上传类型来确定资源访问地址resourceUrl的值
if (Objects.equals(imgUploadUtil.getUploadType(), 2)) {
// 如果上传类型为2从注入的Qiniu对象中获取资源访问地址可能是七牛云存储资源的访问地址用于构建完整的图片访问路径
resourceUrl = qiniu.getResourcesUrl();
} else if (Objects.equals(imgUploadUtil.getUploadType(), 1)) {
// 如果上传类型为1从ImgUploadUtil对象中获取资源访问地址可能是其他存储方式对应的资源访问地址
resourceUrl = imgUploadUtil.getResourceUrl();
}
// 遍历分割后的每个图片路径字符串
for (String img : imgs) {
// 使用创建的Pattern对象对当前图片路径字符串进行正则匹配得到一个Matcher对象用于查看是否匹配成功
Matcher matcher = pattern.matcher(img);
// 如果匹配成功,说明图片路径已经是以"http"或"https"开头的完整网络访问地址形式,直接将其添加到字符串构建器中,并添加逗号(保持与传入格式的一致性,后续会处理末尾多余的逗号)
if (matcher.find()) {
sb.append(img).append(StrUtil.COMMA);
} else {
// 如果图片路径不是完整的网络访问地址形式则将前面确定的资源访问地址resourceUrl、当前图片路径以及逗号依次添加到字符串构建器中构建完整的图片访问路径格式
sb.append(resourceUrl).append(img).append(StrUtil.COMMA);
}
}
// 删除字符串构建器中最后一个字符(末尾多余的逗号),得到最终处理好的图片路径字符串
sb.deleteCharAt(sb.length() - 1);
// 将处理好的图片路径字符串通过JsonGenerator写入到最终的JSON输出中完成序列化操作
gen.writeString(sb.toString());
}
}

@ -0,0 +1,168 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
*
* doubleBigDecimal
* @author lanhai
*/
public class Arith {
/**
* div2
* HALF_EVEN
*/
private static final int DEF_DIV_SCALE = 2;
/**
*
*
*/
private Arith() {
}
/**
*
* doubleBigDecimal
* double使double
*
* @param v1
* @param v2
* @return double
*/
public static double add(double v1, double v2) {
// 必须转换成String因为BigDecimal的构造函数建议使用基于字符串的构造方式来避免精度问题
// 如果直接使用double类型传入构造函数在某些情况下可能会出现精度丢失。
String s1 = Double.toString(v1);
String s2 = Double.toString(v2);
BigDecimal b1 = new BigDecimal(s1);
BigDecimal b2 = new BigDecimal(s2);
return b1.add(b2).doubleValue();
}
/**
*
* doubleBigDecimal
* double
*
* @param v1
* @param v2
* @return double
*/
public static double sub(double v1, double v2) {
String s1 = Double.toString(v1);
String s2 = Double.toString(v2);
BigDecimal b1 = new BigDecimal(s1);
BigDecimal b2 = new BigDecimal(s2);
return b1.subtract(b2).doubleValue();
}
/**
*
* doubleBigDecimal
* double
*
* @param v1
* @param v2
* @return double
*/
public static double mul(double v1, double v2) {
String s1 = Double.toString(v1);
String s2 = Double.toString(v2);
BigDecimal b1 = new BigDecimal(s1);
BigDecimal b2 = new BigDecimal(s2);
return b1.multiply(b2).doubleValue();
}
/**
* 10
* divDEF_DIV_SCALE
*
*
* @param v1
* @param v2
* @return double
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* scale
* scale0
* 0doubleBigDecimal
* 使HALF_EVENdouble
*
* @param v1
* @param v2
* @param scale
* @return double
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
String s1 = Double.toString(v1);
String s2 = Double.toString(v2);
BigDecimal b1 = new BigDecimal(s1);
BigDecimal b2 = new BigDecimal(s2);
return b1.divide(b2, scale, RoundingMode.HALF_EVEN).doubleValue();
}
/**
*
* scale0
* BigDecimal1BigDecimal
* scaleHALF_EVENdouble
*
* @param v double
* @param scale
* @return double
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
String s = Double.toString(v);
BigDecimal b = new BigDecimal(s);
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, RoundingMode.HALF_EVEN).doubleValue();
}
/**
* BigDecimal
* double便BigDecimal使
*
* @param bigDecimal BigDecimal
* @param bigDecimal2 BigDecimal
* @param bigDecimal3 BigDecimal
* @return double
*/
public static double add(BigDecimal bigDecimal, BigDecimal bigDecimal2, BigDecimal bigDecimal3) {
return bigDecimal.add(bigDecimal2).add(bigDecimal3).doubleValue();
}
/**
* BigDecimal
* doubleBigDecimal
*
* @param preDepositPrice BigDecimal
* @param finalPrice BigDecimal
* @return double
*/
public static double add(BigDecimal preDepositPrice, BigDecimal finalPrice) {
return preDepositPrice.add(finalPrice).doubleValue();
}
}

@ -0,0 +1,87 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
import lombok.AllArgsConstructor;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
/**
* CacheManagerUtil便Spring
* SpringCacheManager
* 便
* Spring@Component使
*
* @author lanhai
*/
@Component
@AllArgsConstructor
public class CacheManagerUtil {
// Spring的缓存管理器用于获取具体的缓存对象等操作通过构造注入的方式获取
private CacheManager cacheManager;
/**
*
*
* @param <T>
* @param cacheName CacheManager
* @param key
* @return Tnull
*/
@SuppressWarnings({"unchecked"})
public <T> T getCache(String cacheName, String key) {
// 通过缓存管理器获取指定名称的缓存对象
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
return null;
}
// 从缓存对象中尝试获取对应键的值的包装对象
Cache.ValueWrapper valueWrapper = cache.get(key);
if (valueWrapper == null) {
return null;
}
// 从包装对象中获取实际的值并转换为指定的泛型类型T返回
return (T) valueWrapper.get();
}
/**
*
*
* @param cacheName CacheManager
* @param key
* @param value Java
*/
public void putCache(String cacheName, String key, Object value) {
// 通过缓存管理器获取指定名称的缓存对象
Cache cache = cacheManager.getCache(cacheName);
if (cache!= null) {
// 如果缓存对象存在,则将键值对存入该缓存对象中
cache.put(key, value);
}
}
/**
*
*
* @param cacheName CacheManager
* @param key
*/
public void evictCache(String cacheName, String key) {
// 通过缓存管理器获取指定名称的缓存对象
Cache cache = cacheManager.getCache(cacheName);
if (cache!= null) {
// 如果缓存对象存在,则清除对应键的缓存数据
cache.evict(key);
}
}
}

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入Spring相关的类用于获取请求相关的上下文信息基于请求上下文来获取HttpServletRequest对象
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
// 导入Servlet相关的类用于操作HTTP请求这里主要是HttpServletRequest它包含了请求相关的各种信息
import jakarta.servlet.http.HttpServletRequest;
/**
* HttpContextUtilsHTTP便
* SpringHttpServletRequest
* 便使
*
* @author lanhai
*/
public class HttpContextUtils {
/**
* 线HttpServletRequest
* SpringRequestContextHolderServletRequestAttributes
* HttpServletRequest
*
* @return HttpServletRequest HttpServletRequest
*
*/
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
/**
*
* getHttpServletRequestHttpServletRequest
* getRequestURLURL
* getRequestURIURL
*
* @return String http://example.com:8080具体取决于实际请求情况
*/
public static String getDomain() {
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
/**
* Origin
* HttpServletRequestgetHeader"Origin"
*
*
* @return String Originhttp://example.com如果有Origin请求头的话否则返回null等情况
*/
public static String getOrigin() {
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}

@ -0,0 +1,127 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入 Hutool 库中用于生成分布式唯一ID的 Snowflake 类,通常基于雪花算法实现
import cn.hutool.core.lang.Snowflake;
// 导入 Spring 框架用于实现依赖注入的注解以及将类标记为组件的注解,表明该类是受 Spring 管理的组件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* IdUtil
* 1. DICT DICT.length()
* 2. ID便使 ID
* 3. ID ID
* 4. Snowflake ID ID
*
* @author xuliugen
* @date 2018/04/23
*/
@Component
public class IdUtil {
// 通过 Spring 的依赖注入机制,注入一个 Snowflake 实例,用于生成分布式唯一 ID可能用于生成短 ID 的基础)
@Autowired
private Snowflake snowflake;
// 定义了一个包含数字和大小写字母(去除了容易混淆的部分字母)的字符串,作为自定义进制的字符集,用于进制转换操作
private static final String DICT = "0123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
// 计算出基于 DICT 字符集的进制数,即字符集的长度,用于进制转换过程中的计算
private static final int SEED = DICT.length();
// 定义了短网址编码的最小长度,用于在生成短网址编码时,如果长度不足则进行补位操作,保证编码长度符合一定要求
private static final int ID_MIN_LENGTH = 6;
/**
* DICT 便
*/
private static final char[] CHARS = DICT.toCharArray();
/**
* Map
*/
private static final Map<Character, Integer> NUMBERS = new HashMap<>();
// 静态代码块,用于初始化 NUMBERS 这个字符到数字的映射 Map遍历 CHARS 字符数组,将每个字符与其对应的索引(在自定义进制下的数字)存入 Map 中
static {
int len = CHARS.length;
for (int i = 0; i < len; i++) {
NUMBERS.put(CHARS[i], i);
}
}
/**
* ID DICT
* SEED DICT
* ID_MIN_LENGTH DICT
*
* @param id ID (1 - 56.8 billion)
* @return "RwTji8""GijT7Y"
*/
public static String encode(long id) {
// 创建一个可变的字符串构建器,用于逐步构建短网址编码字符串
StringBuilder shortUrl = new StringBuilder();
// 当传入的十进制数字大于 0 时,进行进制转换操作,采用除基取余法
while (id > 0) {
// 计算当前十进制数除以自定义进制数SEED的余数将其转换为整数类型该余数将作为在 DICT 中查找对应字符的索引
int r = (int) (id % SEED);
// 将根据余数获取到的对应字符插入到短网址编码字符串的开头(逆序构建编码字符串)
shortUrl.insert(0, CHARS[r]);
// 更新十进制数,将其除以自定义进制数,得到下一轮循环要处理的数字
id = id / SEED;
}
// 获取当前已经构建好的短网址编码字符串的长度
int len = shortUrl.length();
// 如果长度小于最小长度要求ID_MIN_LENGTH进行补位操作
while (len < ID_MIN_LENGTH) {
// 在短网址编码字符串的开头插入 DICT 中的第一个字符(通常是 '0')进行补位
shortUrl.insert(0, CHARS[0]);
// 更新长度
len++;
}
// 返回最终构建好的短网址编码字符串
return shortUrl.toString();
}
/**
* ID
*
*
* @param key "RwTji8""GijT7Y"
* @return ID
*/
public static long decode(String key) {
// 将传入的短网址编码字符串转换为字符数组,方便逐个字符进行处理
char[] shorts = key.toCharArray();
// 获取字符数组的长度,即短网址编码的长度
int len = shorts.length;
// 初始化用于累加计算的十进制数字为 0
long id = 0L;
// 遍历短网址编码的每个字符,从左到右(按照权重从高到低)进行解析计算
for (int i = 0; i < len; i++) {
// 根据当前字符在 NUMBERS 映射 Map 中获取其对应的数字在自定义进制下的数字表示并乘以当前位置对应的权重SEED 的幂次方),然后累加到结果中
id = id + (long) (NUMBERS.get(shorts[i]) * Math.pow(SEED, len - i - 1));
}
// 返回解析得到的十进制数字(数据库记录 ID
return id;
}
/**
* Snowflake ID ID
* ID
*
* @return ID ID
*/
public String nextShortId() {
return encode(snowflake.nextId());
}
}

@ -0,0 +1,127 @@
package com.yami.shop.common.util;
// 导入Hutool工具库中用于字符串操作的工具类可用于判断字符串是否为空等操作
import cn.hutool.core.util.StrUtil;
// 导入自定义的用于封装图片上传相关配置信息的类包含存储路径、上传类型、资源访问URL等属性
import com.yami.shop.common.bean.ImgUpload;
// 导入自定义的业务异常类,用于在特定业务逻辑出现问题时抛出相应的异常信息,方便统一处理
import com.yami.shop.common.exception.YamiShopBindException;
// 导入Spring框架用于实现依赖注入的注解以及将类标记为组件的注解表明该类是受Spring管理的组件可在项目中被自动注入和使用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
// 导入Spring用于处理文件上传的核心类代表上传的文件对象包含文件内容、文件名等信息
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
/**
* ImgUploadUtil
* 访URL
* 便
*
* @author TRACK
*/
@Component
public class ImgUploadUtil {
// 通过Spring的依赖注入机制注入一个ImgUpload对象该对象包含了本地文件上传相关的配置信息如存储路径、上传方式等
@Autowired
private ImgUpload imgUpload;
/**
* ImgUpload
* nullYamiShopBindException
*
*
* @return Integer 12使
*/
public Integer getUploadType() {
Integer uploadType = imgUpload.getUploadType();
if (Objects.isNull(uploadType)) {
throw new YamiShopBindException("请配置图片存储方式");
}
return uploadType;
}
/**
* ImgUploadimagePath
* nullYamiShopBindException
*
*
* @return String
*/
public String getUploadPath() {
String imagePath = imgUpload.getImagePath();
if (Objects.isNull(imagePath) || StrUtil.isBlank(imagePath)) {
throw new YamiShopBindException("请配置图片存储路径");
}
return imagePath;
}
/**
* 访URLImgUpload访URLresourceUrl
* null访URLYamiShopBindException
* 访URLURL访
*
* @return String 访URL访
*/
public String getResourceUrl() {
String resourceUrl = imgUpload.getResourceUrl();
if (Objects.isNull(resourceUrl) || StrUtil.isBlank(resourceUrl)) {
throw new YamiShopBindException("请配置图片路径");
}
return resourceUrl;
}
/**
* MultipartFile
*
* I/O
*
*
* @param img Spring
* @param fileName
* @return String 访
*/
public String upload(MultipartFile img, String fileName) {
// 获取配置的本地文件上传路径(存储文件夹路径)
String filePath = imgUpload.getImagePath();
// 根据文件上传路径和传入的文件名构建一个File对象代表要保存的目标文件
File file = new File(filePath + fileName);
// 判断目标文件所在的目录是否存在,如果不存在则尝试创建目录
if (!file.exists()) {
boolean result = file.mkdirs();
// 如果目录创建失败返回false抛出自定义的业务异常提示创建目录失败的具体路径信息
if (!result) {
throw new YamiShopBindException("创建目录:" + filePath + "失败");
}
}
try {
// 将上传的文件内容转移到目标文件中即将MultipartFile中的文件数据写入到本地创建好的目标文件里
img.transferTo(file);
} catch (IOException e) {
// 如果在文件转移写入过程中出现I/O异常抛出自定义的业务异常提示图片上传失败
throw new YamiShopBindException("图片上传失败");
}
// 文件上传成功后,返回上传后的文件名,方便后续业务使用
return fileName;
}
/**
*
* deleteOnExitJVM退
* JVM退
*
* @param fileName
*/
public void delete(String fileName) {
// 获取配置的本地文件上传路径(存储文件夹路径)
String filePath = imgUpload.getImagePath();
// 根据文件上传路径和传入的文件名构建一个File对象代表要删除的目标文件
File file = new File(filePath + fileName);
// 标记文件在JVM退出时删除注意这并不一定会立即删除文件而是在JVM正常退出时执行删除操作如果文件存在且可删除的话
file.deleteOnExit();
}
}

@ -0,0 +1,67 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入Servlet相关的用于操作HTTP请求的类后续用于获取请求头以及客户端IP地址等信息
import jakarta.servlet.http.HttpServletRequest;
/**
* IpHelperIP
* IP
* IP
* IPIP
*
* @author lanhai
*/
public class IpHelper {
// 定义一个表示未知IP地址的常量字符串用于后续判断请求头中获取到的IP地址是否有效
private static final String UNKNOWN = "unknown";
/**
* IPIP
* "x-forwarded-for"IPIP
* IP0"unknown""Proxy-Client-IP"
* "WL-Proxy-Client-IP"
* IPRemoteAddrIP
* IPIPIPIP
*
* @return String IPnull
*/
public static String getIpAddr() {
// 通过HttpContextUtils工具类获取当前线程绑定的HttpServletRequest对象该对象包含了请求相关的各种信息
HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
// 如果获取到的HttpServletRequest对象为null说明无法获取到请求上下文相关信息直接返回null无法获取IP地址
if (request == null) {
return null;
}
// 首先尝试从"x-forwarded-for"请求头获取IP地址该请求头在经过代理服务器转发时可能包含客户端真实IP
String ip = request.getHeader("x-forwarded-for");
// 如果获取到的IP地址为空、长度为0或者等于表示未知的字符串不区分大小写比较则继续尝试从其他请求头获取
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
// 如果从"Proxy-Client-IP"请求头获取到的IP地址仍不符合要求为空、长度为0或者是未知字符串则再尝试从"WL-Proxy-Client-IP"请求头获取
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
// 如果经过前面的尝试还是没有获取到有效IP地址则直接获取请求的远程地址RemoteAddr作为IP地址这是最基本的获取客户端IP的方式
if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 将获取到的IP地址字符串按照逗号进行分割因为可能存在经过多层代理IP地址有多个的情况以逗号分隔
String[] ips = ip.split(",");
// 返回分割后的第一个IP地址去除两端的空白字符作为客户端的真实IP地址如果只有一个IP则就是该IP本身
return ips[0].trim();
}
}

@ -0,0 +1,148 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入Jackson相关的注解和类用于配置JSON序列化和反序列化过程中的一些行为比如包含哪些属性、如何处理未知属性等
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
// 导入Lombok的日志记录相关注解用于简化日志记录代码自动生成名为log的日志记录对象
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* JsonJackson便JSON
* JavaJSONJSONJavaJSON
* JacksonObjectMapperJSON
*
* @author lanhai
*/
@Slf4j
public class Json {
// 创建一个静态的ObjectMapper实例ObjectMapper是Jackson库中用于进行JSON序列化和反序列化的核心类后续所有的JSON操作都基于它来实现
private static ObjectMapper objectMapper = new ObjectMapper();
// 静态代码块用于对ObjectMapper实例进行一系列的配置这些配置会影响JSON序列化和反序列化的行为。
static {
// 设置序列化时的包含规则这里配置为JsonInclude.Include.NON_EMPTY表示如果属性值为空比如null、空字符串、空集合等则不输出该属性到JSON字符串中减少不必要的JSON数据量。
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
// 配置在序列化时对于空的Java对象没有任何属性值的对象转JSON的时候不抛出错误而是正常返回一个空的JSON对象如 {}),增强程序的健壮性,避免因空对象序列化失败导致整个操作中断。
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 禁用将日期类型属性序列化为时间戳的功能,这样在处理日期类型数据时可以按照更符合业务需求的日期格式(比如特定的字符串格式)进行序列化,而不是默认的时间戳形式。
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 禁用在反序列化时遇到未知属性即JSON字符串中的属性在对应的Java类中不存在定义抛出异常的功能这样即使JSON数据有额外的属性也能尽量解析出已知的属性值避免因未知属性导致整个反序列化失败。
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 取消对非ASCII字符的转码操作使得JSON字符串中可以直接包含如中文等非ASCII字符而不需要进行转义编码方便查看和处理更符合实际业务场景中对中文等字符的使用需求。
objectMapper.configure(JsonWriteFeature.ESCAPE_NON_ASCII.mappedFeature(), false);
}
/**
* JavaJSON
* ObjectMapperwriteValueAsStringJSON
* null
*
* @param object JSONJavaJacksonPOJO
* @return String JSONnull
*/
public static String toJsonString(Object object) {
try {
return objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
// 记录对象转JSON时出现的错误日志方便后续排查问题这里记录了异常的详细信息通过传入e作为参数
log.error("对象转json错误", e);
}
return null;
}
/**
* JSONJava
* ObjectMapperreadValueclazzJSONJava
* JSONnull
*
*
* @param json JSONJavaJackson
* @param clazz JavaObjectMapperJSON
* @return <T> Javaclazznull
*/
public static <T> T parseObject(String json, Class<T> clazz) {
T result = null;
try {
result = objectMapper.readValue(json, clazz);
} catch (Exception e) {
// 记录JSON转对象时出现的错误日志方便后续排查问题这里记录了异常的详细信息通过传入e作为参数
log.error("对象转json错误", e);
}
return result;
}
/**
* ObjectMapperJSON
* JSON使JSON
*
* @return ObjectMapper ObjectMapperJSON
*/
public static ObjectMapper getObjectMapper() {
return objectMapper;
}
/**
* JSONJavaList
* ObjectMapperreadValueJSONclazz
* ListnullList
* 便
* 使TypeReference10
*
* @param json JSONJavaJSON
* @param clazz JavaObjectMapperJSONMyClass[].class
* @return <T> JavaclazzList
*/
public static <T> List<T> parseArray(String json, Class<T[]> clazz) {
T[] result = null;
try {
result = objectMapper.readValue(json, clazz);
} catch (Exception e) {
// 记录JSON转换时出现的错误日志方便后续排查问题这里记录了异常的详细信息通过传入e作为参数
log.error("Json转换错误", e);
}
if (result == null) {
return Collections.emptyList();
}
return Arrays.asList(result);
}
/**
* JSONJsonNodeJsonNodeJSON便JSON
* JSONJSONMap
* null
*
* @param jsonStr JsonNodeJSONJSON
* @return JsonNode JsonNodenullJSON
*/
public static JsonNode parseJson(String jsonStr) {
JsonNode jsonNode = null;
try {
jsonNode = objectMapper.readTree(jsonStr);
} catch (Exception e) {
// 记录JSON转换时出现的错误日志方便后续排查问题这里记录了异常的详细信息通过传入e作为参数
log.error("Json转换错误", e);
}
return jsonNode;
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入Hutool工具库中用于分页相关操作的工具类这里主要用于将页码、每页数量等信息转换为数据库查询中起始位置和结束位置的相关操作
import cn.hutool.core.util.PageUtil;
// 导入MyBatis Plus框架中用于表示分页信息的核心类包含了当前页码、每页显示数量等分页相关的属性和方法
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 引入Lombok的Data注解通过该注解编译器会自动帮我们生成类的常用方法比如各属性的Getter、Setter方法以及toString、equals、hashCode方法等简化代码编写
import lombok.Data;
/**
* PageAdapterMyBatis PlusPage
*
* beginsize便使
* Hutool
*
* @author lh
*/
@Data
public class PageAdapter {
// 用于表示分页查询时的起始位置(通常对应数据库查询中的偏移量,从第几条记录开始查询)
private int begin;
// 用于表示分页查询时每页的记录数量,即每页显示多少条数据
private int size;
/**
* MyBatis PlusPage
* HutoolPageUtiltransToStartEnd
* Pagebeginsize
*
* @param page MyBatis PlusPagebeginsize
*/
public PageAdapter(Page page) {
// 调用Hutool的PageUtil工具类的transToStartEnd方法将当前页码需要减1因为数据库查询中页码通常从0开始计数和每页数量转换为起始位置和结束位置的数组这里取数组的第一个元素作为起始位置begin
int[] startEnd = PageUtil.transToStartEnd((int) page.getCurrent() - 1, (int) page.getSize());
this.begin = startEnd[0];
// 将传入的Page对象中的每页数量赋值给size属性作为每页的记录数量
this.size = (int) page.getSize();
}
}

@ -0,0 +1,213 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.common.util;
// 导入MyBatis Plus框架中用于表示分页信息的核心类包含了分页相关的属性如当前页、每页大小等以及操作方法如获取记录列表、设置总数等
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入Jackson相关的注解用于在JSON序列化和反序列化过程中忽略指定的属性这里用于标记某些属性在转换为JSON时不进行处理
import com.fasterxml.jackson.annotation.JsonIgnore;
// 导入Swagger相关的注解用于在生成API文档时隐藏某些属性使得这些属性不在文档中展示出来一般用于内部使用或不需要对外暴露的属性
import io.swagger.v3.oas.annotations.Hidden;
// 导入Swagger相关的注解用于在生成API文档时为类、属性等添加描述信息方便前端开发人员等查看其含义和作用
import io.swagger.v3.oas.annotations.media.Schema;
// 导入SpringDoc相关的注解用于标记该类作为参数对象在生成API文档以及进行参数校验等场景下可以被识别和处理
import org.springdoc.core.annotations.ParameterObject;
import java.util.List;
/**
* PageParamMyBatis PlusPage
* PageJSON
* Page
* count
*
* @author lanhai
*/
@Schema
@ParameterObject
public class PageParam<T> extends Page<T> {
/**
* 10
* @SchemaAPI便使
*/
@Schema(description = "每页大小默认10")
private long size = 10;
/**
* 1@Schema便API
*/
@Schema(description = "当前页默认1")
private long current = 1;
/**
* @HiddenAPI使
*/
@Hidden
private List<T> records;
/**
* @HiddenAPI
*/
@Hidden
private long total = 0;
/**
* counttruecount
* @JsonIgnoreJSON
*/
@JsonIgnore
private boolean isSearchCount = true;
/**
* @JsonIgnoreJSON使
*/
@JsonIgnore
private String countId;
/**
* 100@JsonIgnoreJSON
*/
@JsonIgnore
private Long maxLimit;
/**
* countSQL@JsonIgnoreJSON使
*/
@JsonIgnore
private boolean optimizeCountSql;
/**
* Pagerecords
*
* @return List<T> T
*/
@Override
public List<T> getRecords() {
return this.records;
}
/**
* Pagerecordsthis便
* 便
*
* @param records T
* @return Page<T>
*/
@Override
public Page<T> setRecords(List<T> records) {
this.records = records;
return this;
}
/**
* Pagetotal使
*
* @return long
*/
@Override
public long getTotal() {
return this.total;
}
/**
* Pagetotalthis便
* count
*
* @param total
* @return Page<T>
*/
@Override
public Page<T> setTotal(long total) {
this.total = total;
return this;
}
/**
* countisSearchCount
* total0falsecount
* isSearchCountcount
*
* @return boolean counttruecountfalse
*/
@JsonIgnore
public boolean getSearchCount() {
if (total < 0) {
return false;
}
return isSearchCount;
}
/**
* PagecountisSearchCountthis便
* count
*
* @param isSearchCount counttruefalse
* @return Page<T>
*/
@Override
public Page<T> setSearchCount(boolean isSearchCount) {
this.isSearchCount = isSearchCount;
return this;
}
/**
* Pagesize使
*
* @return long
*/
@Override
public long getSize() {
return this.size;
}
/**
* Page
* size100size100
* sizesizethis便
*
* @param size
* @return Page<T>
*/
@Override
public Page<T> setSize(long size) {
int maxSize = 100;
if (size > maxSize) {
this.size = maxSize;
} else {
this.size = size;
}
return this;
}
/**
* Pagecurrent使
*
* @return long
*/
@Override
public long getCurrent() {
return this.current;
}
/**
* Pagecurrentthis便
*
*
* @param current
* @return Page<T>
*/
@Override
public Page<T> setCurrent(long current) {
this.current = current;
return this;
}
}

@ -0,0 +1,27 @@
package com.yami.shop.security.admin.dto;
import com.yami.shop.security.common.dto.AuthenticationDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* CaptchaAuthenticationDTODTO
* AuthenticationDTO
*
*
*
* @author
* @date 2022/3/28 14:57
*/
@Data
public class CaptchaAuthenticationDTO extends AuthenticationDTO {
/**
* captchaVerification
* @SchemaSwagger
* required = true
* 便
*/
@Schema(description = "验证码", required = true)
private String captchaVerification;
}

@ -0,0 +1,136 @@
package com.yami.shop.security.api.controller;
// 导入MyBatis Plus框架中用于构建条件查询的核心类方便编写数据库查询条件此处用于查询用户信息
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
// 导入项目中自定义的用户实体类,代表数据库中存储的用户相关信息,包含各种用户属性,如用户名、手机号、密码等
import com.yami.shop.bean.model.User;
// 导入项目自定义的业务异常类,用于在业务逻辑出现特定错误情况时抛出异常,方便统一处理和向客户端返回错误信息
import com.yami.shop.common.exception.YamiShopBindException;
// 导入项目自定义的工具类,可能包含一些通用的业务逻辑处理方法,此处具体功能需看其内部实现(从名字推测和主体逻辑相关)
import com.yami.shop.common.util.PrincipalUtil;
// 导入项目中定义的用于操作数据库中用户表的Mapper接口通过它可以调用数据库相关的查询、插入等操作方法由MyBatis框架生成实现类来具体执行SQL操作
import com.yami.shop.dao.UserMapper;
// 导入与安全相关的业务对象类用于封装在Token中存储的用户信息方便在不同模块间传递和使用用户相关数据
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
// 导入与安全相关的用于接收登录认证信息的数据传输对象DTO包含用户名、密码以及用户所属系统类型等信息用于接收前端传入的登录数据
import com.yami.shop.security.common.dto.AuthenticationDTO;
// 导入与安全相关的枚举类用于区分不同的系统类型此处有个普通用户类型ORDINARY可能还有其他类型用于不同业务场景下的用户分类
import com.yami.shop.security.common.enums.SysTypeEnum;
// 导入与安全相关的用于密码检查的管理类,可能包含验证密码是否符合规则、检查密码错误次数等逻辑,保障登录密码的安全性
import com.yami.shop.security.common.manager.PasswordCheckManager;
// 导入与安全相关的用于密码管理的类,可能包含密码加密、解密等相关操作方法,确保密码在存储和传输过程中的安全性
import com.yami.shop.security.common.manager.PasswordManager;
// 导入与安全相关的用于管理Token存储和操作的类负责生成、存储Token以及基于用户信息获取相应的Token相关信息等功能
import com.yami.shop.security.common.manager.TokenStore;
// 导入与安全相关的用于返回给前端包含Token信息的视图对象VO将后端处理后的Token相关数据以合适的格式返回给前端展示和后续使用
import com.yami.shop.security.common.vo.TokenInfoVO;
// 导入Swagger相关的注解用于生成API文档@Tag注解用于给接口分类添加标签方便文档中进行分组展示和说明
import io.swagger.v3.oas.annotations.tags.Tag;
// 导入Swagger相关的注解用于在API文档中描述接口的功能摘要和详细信息方便前端开发人员等查看接口的作用和使用方式
import io.swagger.v3.oas.annotations.Operation;
// 导入Spring框架用于实现依赖注入的注解表明后续的属性需要由Spring容器进行注入实例化
import org.springframework.beans.factory.annotation.Autowired;
// 导入项目自定义的用于封装服务器响应信息的实体类,包含响应状态码、消息、数据等内容,用于统一向客户端返回响应结果
import com.yami.shop.common.response.ServerResponseEntity;
// 导入Spring框架用于定义处理POST请求的注解将下面的方法映射为一个接收POST请求的接口处理对应的业务逻辑
import org.springframework.web.bind.annotation.PostMapping;
// 导入Spring框架用于接收请求体数据并将其绑定到方法参数的注解确保前端传入的JSON等格式的数据能正确映射到对应的参数对象上
import org.springframework.web.bind.annotation.RequestBody;
// 导入Spring框架用于将类标记为RESTful风格的控制器的注解表明该类中的方法主要用于处理HTTP请求并返回JSON等格式的响应数据
import org.springframework.web.bind.annotation.RestController;
// 导入Jakarta验证相关的注解用于对传入的参数对象进行数据校验确保参数符合一定的规则比如非空等要求
import jakarta.validation.Valid;
/**
* LoginControllerSpring RESTful
*
* Token
* 便使SwaggerAPI便使
*
* @author
* @date 2022/3/28 15:20
*/
@RestController
@Tag(name = "登录")
public class LoginController {
// 通过Spring的依赖注入机制注入一个TokenStore实例用于管理Token的存储、生成以及获取相关信息等操作
@Autowired
private TokenStore tokenStore;
// 注入一个UserMapper实例用于调用数据库中用户表相关的查询、操作方法以便获取用户信息进行登录验证等操作
@Autowired
private UserMapper userMapper;
// 注入一个PasswordCheckManager实例用于对用户输入的密码进行各种检查如密码错误次数限制等安全相关的验证操作
@Autowired
private PasswordCheckManager passwordCheckManager;
// 注入一个PasswordManager实例用于对密码进行加密、解密等管理操作此处主要用于解密前端传入的密码进行后续验证
@Autowired
private PasswordManager passwordManager;
/**
* @Valid
*
* Token
* TokenStoreTokenTokenTokenInfoVOToken
*
* @param authenticationDTO @RequestBody
* @return ServerResponseEntity<TokenInfoVO> TokenTokenInfoVOToken
*/
@PostMapping("/login")
@Operation(summary = "账号密码(用于前端登录)", description = "通过账号/手机号/用户名密码登录,还要携带用户的类型,也就是用户所在的系统")
public ServerResponseEntity<TokenInfoVO> login(
@Valid @RequestBody AuthenticationDTO authenticationDTO) {
// 从登录认证信息对象中获取用户名(可能是手机号或者普通用户名形式),用于后续查找用户信息
String mobileOrUserName = authenticationDTO.getUserName();
// 根据获取到的用户名查找对应的用户信息,若找不到则会抛出异常告知账号或密码不正确
User user = getUser(mobileOrUserName);
// 通过PasswordManager对前端传入的密码进行解密操作获取解密后的密码以便后续和数据库中存储的密码进行比对验证
String decryptPassword = passwordManager.decryptPassword(authenticationDTO.getPassWord());
// 通过PasswordCheckManager检查密码是否正确以及是否达到密码错误次数限制半小时内密码输入错误十次已限制登录30分钟若不符合要求会抛出相应异常进行处理
passwordCheckManager.checkPassword(SysTypeEnum.ORDINARY, authenticationDTO.getUserName(), decryptPassword, user.getLoginPassword());
// 创建一个用于在Token中存储用户信息的业务对象将用户的ID、所属系统类型以及是否启用等信息设置进去用于后续生成Token以及存储相关用户标识信息
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
userInfoInToken.setUserId(user.getUserId());
userInfoInToken.setSysType(SysTypeEnum.ORDINARY.value());
userInfoInToken.setEnabled(user.getStatus() == 1);
// 通过TokenStore存储用户信息并生成Token同时获取包含Token相关信息的视图对象TokenInfoVO用于返回给前端告知登录成功以及提供后续鉴权等所需的Token信息
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
return ServerResponseEntity.success(tokenInfoVO);
}
/**
*
*
*
* YamiShopBindException
*
* @param mobileOrUserName
* @return User null
*/
private User getUser(String mobileOrUserName) {
User user = null;
// 通过PrincipalUtil工具类判断传入的用户名是否符合手机号格式如果是则进行下一步操作通过手机号查找用户
if (PrincipalUtil.isMobile(mobileOrUserName)) {
// 使用MyBatis Plus构建的条件查询对象LambdaQueryWrapper根据手机号在数据库中查找对应的用户信息通过UserMapper调用数据库查询方法
user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserMobile, mobileOrUserName));
}
// 如果通过手机号没找到用户即user为null则尝试通过用户名在数据库中查找用户
if (user == null) {
user = userMapper.selectOneByUserName(mobileOrUserName);
}
// 如果最终还是没找到对应的用户,则抛出自定义的业务异常,告知账号或密码不正确
if (user == null) {
throw new YamiShopBindException("账号或密码不正确");
}
return user;
}
}

@ -0,0 +1,37 @@
package com.yami.shop.security.common.adapter;
import java.util.List;
/**
* 便
* 使
*
* @author
* @date 2022/3/25 17:31
*/
public interface AuthConfigAdapter {
/**
* URL
* "/**/ma/**" "ma" 访
*
*/
String MAYBE_AUTH_URI = "/**/ma/**";
/**
*
* 访
*
*
* @return
*/
List<String> pathPatterns();
/**
*
* 访
*
* @return
*/
List<String> excludePathPatterns();
}

@ -0,0 +1,79 @@
package com.yami.shop.security.common.adapter;
import com.anji.captcha.service.CaptchaCacheService;
import com.yami.shop.common.util.RedisUtil;
/**
* CaptchaCacheServiceRedisImplRedis
* CaptchaCacheService
* Redis
* 使Redis
*
* @author
* @date 2022/3/25 17:33
*/
public class CaptchaCacheServiceRedisImpl implements CaptchaCacheService {
/**
* Redis
* RedisUtilsetRedis
* keyvalueexpiresInSecondsRedisUtil
*
* @param key Redis
* @param value Redis
* @param expiresInSeconds RedisRedis
*/
@Override
public void set(String key, String value, long expiresInSeconds) {
RedisUtil.set(key, value, expiresInSeconds);
}
/**
* Redis
* RedisUtilhasKey
* Redistruefalse
*
* @param key Redis
* @return Redistruefalse
*/
@Override
public boolean exists(String key) {
return RedisUtil.hasKey(key);
}
/**
* Redis
* RedisUtildelRedis
* 使Redis
*
* @param key RedisRedis
*/
@Override
public void delete(String key) {
RedisUtil.del(key);
}
/**
* Redis
* RedisUtilgetRedis
* RedisUtil
*
* @param key RedisRedis
* @return RedisRedisUtil
*/
@Override
public String get(String key) {
return RedisUtil.get(key);
}
/**
* 使redis
* Redis便
*
* @return redisRedis
*/
@Override
public String type() {
return "redis";
}
}

@ -0,0 +1,53 @@
package com.yami.shop.security.common.adapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
/**
* DefaultAuthConfigAdapterAuthConfigAdapter
* pathPatternsexcludePathPatterns
*
*
* @author
* @date 2022/3/25 17:33
*/
public class DefaultAuthConfigAdapter implements AuthConfigAdapter {
// 创建一个日志记录器用于记录与该类相关的日志信息方便在运行时排查问题、了解类的执行情况等这里记录器的名称为DefaultAuthConfigAdapter类的全限定名
private static final Logger logger = LoggerFactory.getLogger(DefaultAuthConfigAdapter.class);
/**
* DefaultAuthConfigAdapter
* AuthConfigAdapter使DefaultAuthConfigAdapter
* URL
*/
public DefaultAuthConfigAdapter() {
logger.info("not implement other AuthConfigAdapter, use DefaultAuthConfigAdapter... all url need auth...");
}
/**
* pathPatternsAuthConfigAdapter
* /*
*
*
* @return List<String>/*
*/
@Override
public List<String> pathPatterns() {
return Collections.singletonList("/*");
}
/**
* excludePathPatternsAuthConfigAdapter
* pathPatterns
*
*
* @return List<String>
*/
@Override
public List<String> excludePathPatterns() {
return Collections.emptyList();
}
}

@ -0,0 +1,59 @@
package com.yami.shop.security.common.adapter;
// 导入Spring框架中用于将方法标记为创建Bean的注解通过该注解定义的方法返回的对象会被Spring容器管理可在其他地方进行注入使用
import org.springframework.context.annotation.Bean;
// 导入Spring Security框架中用于配置基于Web的安全相关设置的类通过链式调用的方式可以配置如认证、授权、跨域、会话管理等多个方面的安全策略
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
// 导入Spring Security框架中用于启用Web安全功能的注解表明在当前应用中要开启Spring Security相关的Web安全配置机制
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
// 导入Spring Security框架中用于定义会话创建策略的枚举类此处配置为无状态STATELESS会话策略适用于基于Token的认证方式
import org.springframework.security.config.http.SessionCreationPolicy;
// 导入Spring Security框架中用于构建安全过滤链的核心接口配置好的安全规则最终会形成这样一个过滤链来处理进入应用的请求进行安全相关的检查和过滤操作
import org.springframework.security.web.SecurityFilterChain;
// 导入Spring框架中用于将类标记为组件的注解表明该类是一个Spring管理的组件会被自动扫描并实例化可在其他地方进行依赖注入使用
import org.springframework.stereotype.Component;
// 导入Spring框架中用于处理跨域相关的工具类此处用于判断请求是否是跨域预检请求OPTIONS请求以便对不同类型请求做不同的授权处理
import org.springframework.web.cors.CorsUtils;
/**
* MallWebSecurityConfigurerAdapterSpring SecurityWeb
* Spring Security使Spring Security
* Token
* SecurityFilterChain使
*
* @author
* @date 2022/3/25 17:33
*/
@Component
@EnableWebSecurity
public class MallWebSecurityConfigurerAdapter {
/**
* filterChain@BeanSecurityFilterChainSpring使Bean
*
*
* @param http HttpSecurityHTTP
* @return SecurityFilterChain Web
* @throws Exception HttpSecurity
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// 关闭跨站请求伪造CSRF防护功能因为基于Token的认证方式下通常不需要CSRF防护Token本身就用于身份验证可避免此类安全风险
return http.csrf().disable().cors()
// 配置跨域相关设置,启用跨域支持(后续可能还需要配置更具体的跨域规则等,这里只是开启了基本的支持),
// 接着配置会话管理相关内容将会话创建策略设置为无状态STATELESS意味着服务器不会为客户端创建和维护会话状态
// 适用于基于Token的认证场景每次请求都通过携带Token来验证身份服务器不依赖会话来识别客户端
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 配置请求授权相关规则对于跨域预检请求OPTIONS请求通过CorsUtils::isPreFlightRequest判断允许所有的此类请求通过
// 因为跨域预检请求主要是浏览器在正式发起跨域请求前进行的一种询问服务器是否允许跨域的请求,通常需要放行
.and().authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
// 继续配置请求授权规则,对于所有其他请求(通过"/**"表示任意路径的请求也都允许通过这意味着在这个配置下没有进行严格的基于Spring Security自带认证授权的访问限制
// 可能后续会通过其他自定义的中间件或者业务逻辑来对请求进行更细致的权限判断等操作,此处只是简单放开了所有请求的访问权限
.and().authorizeRequests().requestMatchers(
"/**").permitAll().and().build();
}
}

@ -0,0 +1,84 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.config;
import cn.hutool.core.util.ArrayUtil;
// 导入自定义的授权配置适配器类,用于适配不同的授权配置相关逻辑,可根据具体业务需求进行扩展或定制
import com.yami.shop.security.common.adapter.AuthConfigAdapter;
// 导入默认的授权配置适配器实现类,当没有自定义的授权配置适配器时,会使用这个默认的实现类来处理相关逻辑
import com.yami.shop.security.common.adapter.DefaultAuthConfigAdapter;
// 导入自定义的授权过滤器类,用于对请求进行授权相关的过滤操作,比如验证用户权限等
import com.yami.shop.security.common.filter.AuthFilter;
import jakarta.servlet.DispatcherType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
/**
* Spring
*
*
* @author
* @date 2022/3/25 17:33
*/
@Configuration
// 启用方法级别的安全配置,允许在方法上使用诸如 @PreAuthorize、@PostAuthorize 等注解来进行权限控制
@EnableMethodSecurity
public class AuthConfig {
// 通过自动注入获取授权过滤器实例该过滤器将在后续的配置中被注册到Servlet容器中用于对请求进行过滤处理
@Autowired
private AuthFilter authFilter;
/**
* Bean
* SpringAuthConfigAdapterBean
* DefaultAuthConfigAdapter
* 便
*
* @return AuthConfigAdapter
*/
@Bean
@ConditionalOnMissingBean
public AuthConfigAdapter authConfigAdapter() {
return new DefaultAuthConfigAdapter();
}
/**
* FilterRegistrationBean
* FilterRegistrationBeanAuthFilterServlet
*
* 使 @Lazy Bean使
*
* @param authConfigAdapter
* @return FilterRegistrationBean<AuthFilter>Servlet
*/
@Bean
@Lazy
public FilterRegistrationBean<AuthFilter> filterRegistration(AuthConfigAdapter authConfigAdapter) {
FilterRegistrationBean<AuthFilter> registration = new FilterRegistrationBean<>();
// 将之前注入的授权过滤器添加到FilterRegistrationBean中这样Servlet容器就能识别并使用该过滤器来处理请求了
registration.setFilter(authFilter);
// 设置过滤路径,通过授权配置适配器获取需要过滤的路径列表,并转换为字符串数组格式。
// 这里使用ArrayUtil工具类将获取到的路径列表转换为String数组以便设置给FilterRegistrationBean
// 表示该过滤器将会对这些指定的路径下的请求进行过滤处理,此处的 /* 表示所有路径,具体路径配置通常由授权配置适配器决定
registration.addUrlPatterns(ArrayUtil.toArray(authConfigAdapter.pathPatterns(), String.class));
registration.setName("authFilter");
// 设置过滤器的优先级数值越小优先级越高这里设置为0表示较高的优先级在多个过滤器存在的情况下会先执行该过滤器
registration.setOrder(0);
registration.setDispatcherTypes(DispatcherType.REQUEST);
return registration;
}
}

@ -0,0 +1,116 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.config;
import com.anji.captcha.model.common.CaptchaTypeEnum;
import com.anji.captcha.model.common.Const;
import com.anji.captcha.service.CaptchaService;
import com.anji.captcha.service.impl.CaptchaServiceFactory;
import com.anji.captcha.util.ImageUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Base64Utils;
import org.springframework.util.FileCopyUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* CaptchaConfigSpring@Configuration
* Redis
* Redis
* Redis
*
* @author
* @date 2022/3/25 17:33
*/
@Configuration
public class CaptchaConfig {
/**
* captchaServiceSpringBean@Bean
* CaptchaService
* PropertiesCaptchaService
* 使CaptchaServiceFactoryCaptchaService
*
* @return CaptchaServiceSpring使
*/
@Bean
public CaptchaService captchaService() {
Properties config = new Properties();
// 设置验证码的缓存类型为Redis意味着验证码相关的数据如底图等将存储在Redis中
config.put(Const.CAPTCHA_CACHETYPE, "redis");
// 设置验证码的水印为空字符串,即不添加水印(具体根据验证码实现逻辑来定是否生效)
config.put(Const.CAPTCHA_WATER_MARK, "");
// 设置验证码的类型为滑动验证这里使用了CaptchaTypeEnum枚举中BLOCKPUZZLE对应的代码值来指定
config.put(Const.CAPTCHA_TYPE, CaptchaTypeEnum.BLOCKPUZZLE.getCodeValue());
// 设置初始化原始图片相关的标志为true具体含义取决于验证码服务内部对该参数的使用逻辑
config.put(Const.CAPTCHA_INIT_ORIGINAL, "true");
// 调用initializeBaseMap方法进行一些初始化操作可能与验证码底图相关比如预加载等
initializeBaseMap();
return CaptchaServiceFactory.getInstance(config);
}
/**
* initializeBaseMap
* ImageUtilscacheBootImage
* Map
* ImageUtils
* 便便使
*/
private static void initializeBaseMap() {
ImageUtils.cacheBootImage(getResourcesImagesFile("classpath:captcha" + "/original/*.png"),
getResourcesImagesFile("classpath:captcha" + "/slidingBlock/*.png"),
Collections.emptyMap());
}
/**
* getResourcesImagesFileBase64Map
* MapBase64便使
*
*
* @param path "classpath:captcha" + "/original/*.png"png
* @return MapBase64
*/
public static Map<String, String> getResourcesImagesFile(String path) {
Map<String, String> imgMap = new HashMap<>(16);
// 创建一个PathMatchingResourcePatternResolver实例用于解析类路径下的资源模式能够匹配符合指定模式的多个资源文件
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
// 根据传入的路径表达式获取对应的资源数组,即匹配该模式的所有图片资源文件
Resource[] resources = resolver.getResources(path);
// 遍历获取到的所有资源文件
Resource[] var4 = resources;
int var5 = resources.length;
for (int var6 = 0; var6 < var5; ++var6) {
Resource resource = var4[var6];
// 将资源文件的内容读取为字节数组以便后续进行Base64编码操作
byte[] bytes = FileCopyUtils.copyToByteArray(resource.getInputStream());
// 将字节数组转换为Base64编码的字符串使得图片数据可以以文本形式方便地在内存中存储和传输等
String string = Base64Utils.encodeToString(bytes);
// 获取资源文件的文件名用于作为Map中的键来标识对应的Base64编码后的图片数据
String filename = resource.getFilename();
imgMap.put(filename, string);
}
} catch (Exception var11) {
// 如果在获取资源文件或处理过程中出现异常,打印异常堆栈信息,方便排查问题
var11.printStackTrace();
}
return imgMap;
}
}

@ -0,0 +1,61 @@
package com.yami.shop.security.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
/**
* CorsConfigSpring@Configuration
* CORS便
* 使访
*
* @author yami
*/
@Configuration
public class CorsConfig {
/**
* corsConfigurationSourceSpringBean@Bean
* CorsConfigurationSourceSpring WebCORS
* origins访HTTP
*
*
* - *访
* 便访
* - addAllowedOrigin访
* configuration.addAllowedOrigin("http://localhost:8080");
* configuration.addAllowedOrigin("http://192.168.1.6:8080");
*
* @return CorsConfigurationSourceSpring Web使
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
// 创建一个CorsConfiguration对象用于配置跨域相关的各种规则
CorsConfiguration configuration = new CorsConfiguration();
// 使用addAllowedOriginPattern方法添加允许跨域访问的来源模式这里使用通配符*)表示允许所有来源访问,
// 但需注意在生产环境应按实际情况修改为具体域名,可参考上面的注释说明。
configuration.addAllowedOriginPattern("*");
// 修改为添加add而不是设置set允许的HTTP方法使用通配符*表示允许所有的HTTP方法如GET、POST、PUT等
// 这样前端可以使用各种类型的HTTP请求来访问后端接口。
configuration.addAllowedMethod("*");
// 这里配置允许的请求头,同样使用通配符(*)表示允许所有的请求头信息,
// 特别重要的是起码需要允许 Access-Control-Allow-Origin这个请求头它与跨域访问的合法性验证密切相关。
configuration.addAllowedHeader("*");
// 设置是否允许发送Cookie等凭证信息设置为true表示允许
// 若前后端交互需要携带用户认证等相关凭证如Cookie中的登录信息则需要开启此项。
configuration.setAllowCredentials(true);
// 设置预检请求OPTIONS请求的缓存时间单位为秒这里设置为一天3600 * 24秒
// 在缓存有效期内,对于相同来源、方法和请求头的请求,浏览器不会再次发送预检请求,提高性能。
configuration.setMaxAge(3600 * 24L);
// 创建一个基于URL的CorsConfigurationSource对象它可以根据不同的URL路径应用不同的CorsConfiguration配置。
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 将上面配置好的CorsConfiguration应用到所有路径/**表示匹配所有的请求路径),
// 意味着所有的接口请求都会按照这个配置来处理跨域相关的规则。
source.registerCorsConfiguration("/**", configuration);
return source;
}
}

@ -0,0 +1,79 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.controller;
import com.anji.captcha.model.common.RepCodeEnum;
import com.anji.captcha.model.common.ResponseModel;
import com.anji.captcha.model.vo.CaptchaVO;
import com.anji.captcha.service.CaptchaService;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.yami.shop.common.response.ServerResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* CaptchaControllerSpring RESTful@RestController
* HTTP
* "/captcha"@RequestMappingSwagger"验证码"@Tag便
*
* @author
* @date 2022/3/25 17:33
*/
@RestController
@RequestMapping("/captcha")
@Tag(name = "验证码")
public class CaptchaController {
// 通过构造函数注入的方式引入CaptchaService用于后续调用验证码相关的业务逻辑方法比如生成验证码、验证验证码有效性等操作
private final CaptchaService captchaService;
public CaptchaController(CaptchaService captchaService) {
this.captchaService = captchaService;
}
/**
* getHTTP POST
* CaptchaVO@RequestBodyCaptchaVO
* captchaServicegetServerResponseEntity
*
*
* @param captchaVO
* @return ServerResponseEntityResponseModel
*/
@PostMapping({ "/get" })
public ServerResponseEntity<ResponseModel> get(@RequestBody CaptchaVO captchaVO) {
return ServerResponseEntity.success(captchaService.get(captchaVO));
}
/**
* checkHTTP POST
* CaptchaVOCaptchaVO
* captchaServicecheckServerResponseEntity
*
* ResponseModel.errorMsgResponseModel
* ServerResponseEntity
*
* @param captchaVO
* @return ServerResponseEntityResponseModel
*/
@PostMapping({ "/check" })
public ServerResponseEntity<ResponseModel> check(@RequestBody CaptchaVO captchaVO) {
ResponseModel responseModel;
try {
responseModel = captchaService.check(captchaVO);
} catch (Exception e) {
return ServerResponseEntity.success(ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR));
}
return ServerResponseEntity.success(responseModel);
}
}

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.controller;
// 导入Hutool工具库中用于字符串操作的工具类此处用于判断字符串是否为空空白字符串方便进行逻辑判断
import cn.hutool.core.util.StrUtil;
// 导入项目自定义的用于封装服务器响应信息的实体类包含响应状态码、消息、数据等内容用于统一向客户端返回响应结果此处响应数据为Void表示无具体数据返回
import com.yami.shop.common.response.ServerResponseEntity;
// 导入与安全相关的用于管理Token存储和操作的类负责对Token进行删除等相关管理操作此处用于删除用户的登录Token
import com.yami.shop.security.common.manager.TokenStore;
// 导入Swagger相关的注解用于生成API文档@Operation注解用于描述接口的功能摘要和详细信息方便前端开发人员等查看接口的作用和使用方式
import io.swagger.v3.oas.annotations.Operation;
// 导入Swagger相关的注解用于给接口分类添加标签方便文档中进行分组展示和说明此处标记为"注销"类别
import io.swagger.v3.oas.annotations.tags.Tag;
// 导入Spring框架用于实现依赖注入的注解表明后续的属性需要由Spring容器进行注入实例化此处用于注入TokenStore实例
import org.springframework.beans.factory.annotation.Autowired;
// 导入Spring框架用于定义处理POST请求的注解将下面的方法映射为一个接收POST请求的接口处理对应的业务逻辑即处理用户退出登录的请求
import org.springframework.web.bind.annotation.PostMapping;
// 导入Spring框架用于将类标记为RESTful风格的控制器的注解表明该类中的方法主要用于处理HTTP请求并返回JSON等格式的响应数据
import org.springframework.web.bind.annotation.RestController;
// 导入Servlet相关的用于操作HTTP请求的类用于获取请求头中的信息此处主要用于获取包含Token的"Authorization"请求头内容
import jakarta.servlet.http.HttpServletRequest;
/**
* LogoutControllerSpring RESTful退
* 退TokenTokenTokenStoreToken
* 使SwaggerAPI便使
*
* @author
* @date 2022/3/25 17:33
*/
@RestController
@Tag(name = "注销")
public class LogoutController {
// 通过Spring的依赖注入机制注入一个TokenStore实例用于管理Token的删除等相关操作以实现清除用户登录Token的功能
@Autowired
private TokenStore tokenStore;
/**
* 退HTTPHttpServletRequest
* "Authorization"TokenToken
* 退
* TokenTokenStoreToken退
*
* @param request 退HttpServletRequestToken
* @return ServerResponseEntity<Void> Void退
*/
@PostMapping("/logOut")
@Operation(summary = "退出登陆", description = "点击退出登陆清除token清除菜单缓存")
public ServerResponseEntity<Void> logOut(HttpServletRequest request) {
// 从请求头中获取名为"Authorization"的Token信息通常在登录成功后客户端会在后续请求的这个请求头中携带Token用于身份验证等操作
String accessToken = request.getHeader("Authorization");
// 通过Hutool工具类判断获取到的Token信息是否为空空白字符串如果为空则直接返回成功的服务器响应实体表示可视为已退出登录可能本身就没登录
if (StrUtil.isBlank(accessToken)) {
return ServerResponseEntity.success();
}
// 如果获取到了有效的Token信息则调用注入的TokenStore实例的方法删除该用户在当前系统对应的Token实现清除登录状态的功能
tokenStore.deleteCurrentToken(accessToken);
return ServerResponseEntity.success();
}
}

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
/**
* DTO
* 便
*
* @author
* @date 2022/3/25 17:33
*/
@Data
public class AuthenticationDTO {
/**
*
*
* @NotBlank "userName不能为空"
* 使 @Schema API Swagger API //
*/
@NotBlank(message = "userName不能为空")
@Schema(description = "用户名/邮箱/手机号", required = true)
protected String userName;
/**
*
*
* @NotBlank "passWord不能为空"
* @Schema API
*/
@NotBlank(message = "passWord不能为空")
@Schema(description = "一般用作密码", required = true)
protected String passWord;
}

@ -0,0 +1,163 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.filter;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.handler.HttpHandler;
import com.yami.shop.common.response.ResponseEnum;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.security.common.adapter.AuthConfigAdapter;
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
import com.yami.shop.security.common.manager.TokenStore;
import com.yami.shop.security.common.util.AuthUserContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
/**
*
* AuthConfigAdapter访
* 访线
*
* @author
* @date 2022/3/25 17:33
*/
@Component
public class AuthFilter implements Filter {
// 用于记录日志,方便在运行过程中输出相关的调试、错误等信息,便于排查问题
private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);
// 注入AuthConfigAdapter接口的实现类通过它可以获取到配置的需要授权和不需要授权的路径信息
@Autowired
private AuthConfigAdapter authConfigAdapter;
// 注入HttpHandler用于处理向Web端输出响应相关的操作比如将服务器响应信息正确地返回给前端页面
@Autowired
private HttpHandler httpHandler;
// 注入TokenStore主要用于管理和操作与令牌Token相关的存储及查询等功能例如从存储中获取用户信息等
@Autowired
private TokenStore tokenStore;
// 通过配置文件注入令牌名称,用于后续从请求头中获取对应的令牌信息
@Value("${sa-token.token-name}")
private String tokenName;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
/**
*
*
* @param request ServletHttpServletRequestHTTP
* @param response ServletHttpServletResponse
* @param chain ServletJSP
* @throws IOException
* @throws ServletException Servlet
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 将ServletRequest转换为HttpServletRequest方便获取HTTP请求相关的属性和信息比如请求路径、请求头信息等
HttpServletRequest req = (HttpServletRequest) request;
// 将ServletResponse转换为HttpServletResponse便于后续进行HTTP响应相关的操作比如设置响应状态码、响应头、输出响应内容等
HttpServletResponse resp = (HttpServletResponse) response;
// 获取当前请求的URI统一资源标识符用于后续与配置的授权路径进行匹配判断
String requestUri = req.getRequestURI();
// 通过AuthConfigAdapter获取不需要授权的路径列表用于判断当前请求是否命中这些无需授权的路径
List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();
// 创建AntPathMatcher对象用于进行路径的匹配操作它支持通配符等方式来灵活匹配路径
AntPathMatcher pathMatcher = new AntPathMatcher();
// 如果不需要授权的路径列表不为空就遍历这些路径检查当前请求的URI是否匹配其中某个无需授权的路径模式
if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
for (String excludePathPattern : excludePathPatterns) {
// 使用AntPathMatcher进行路径匹配如果匹配成功说明当前请求是不需要授权的直接将请求传递给下一个过滤器或目标资源
if (pathMatcher.match(excludePathPattern, requestUri)) {
chain.doFilter(req, resp);
return;
}
}
}
// 从请求头中获取名为tokenName的令牌信息这个令牌通常用于验证用户的登录状态和权限等
String accessToken = req.getHeader(tokenName);
// 判断当前请求的URI是否匹配可能需要登录但不登录也能用的路径模式通过AuthConfigAdapter中定义的常量路径进行匹配
boolean mayAuth = pathMatcher.match(AuthConfigAdapter.MAYBE_AUTH_URI, requestUri);
// 用于存储从令牌中解析出来的用户信息,如果后续成功获取到用户信息则会赋值
UserInfoInTokenBO userInfoInToken = null;
try {
// 如果获取到的令牌信息不为空(即存在令牌),说明用户可能已经登录,需要进一步验证并获取用户信息
if (StrUtil.isNotBlank(accessToken)) {
// 使用StpUtil工具类来校验用户是否已经登录若登录验证失败会抛出异常在这里捕获异常并进行相应处理
try {
StpUtil.checkLogin();
} catch (Exception e) {
// 如果登录校验失败通过HttpHandler将表示未授权的服务器响应信息输出到Web端通常是返回给前端页面显示相应的错误提示
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
// 如果登录校验通过从TokenStore中根据令牌获取对应的用户信息从缓存等存储中查询并解析用户信息
userInfoInToken = tokenStore.getUserInfoByAccessToken(accessToken, true);
} else if (!mayAuth) {
// 如果没有令牌且当前请求也不属于可能不需要登录就能访问的路径,那么说明该请求需要授权但未提供有效令牌,返回表示未授权的响应给前端
httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
return;
}
// 将获取到的用户信息保存到AuthUserContext中方便在后续的请求处理过程中比如在其他地方需要获取当前登录用户信息时可以直接获取
AuthUserContext.set(userInfoInToken);
// 如果前面的授权校验等操作都通过了将请求传递给下一个过滤器或者最终的目标资源如业务处理的Servlet等继续处理
chain.doFilter(req, resp);
} catch (Exception e) {
// 手动捕获非controller层抛出的异常进行统一处理
if (e instanceof YamiShopBindException) {
// 如果是YamiShopBindException类型的异常通过HttpHandler将该异常对应的响应信息输出到Web端
httpHandler.printServerResponseToWeb((YamiShopBindException) e);
} else {
// 如果是其他类型的异常,直接抛出,让上层的异常处理机制(比如容器的异常处理)去进一步处理
throw e;
}
} finally {
// 无论请求处理过程是否出现异常最终都要清理AuthUserContext中的用户信息避免数据残留影响下一次请求处理
AuthUserContext.clean();
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}

@ -0,0 +1,52 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.manager;
import cn.hutool.crypto.symmetric.AES;
import com.yami.shop.common.exception.YamiShopBindException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
/**
* @author
* @date 2022/1/19 16:02
*/
@Component
public class PasswordManager {
private static final Logger logger = LoggerFactory.getLogger(PasswordManager.class);
/**
* aeskey16
*/
@Value("${auth.password.signKey:-mall4j-password}")
public String passwordSignKey;
public String decryptPassword(String data) {
// 在使用oracle的JDK时JAR包必须签署特殊的证书才能使用。
// 解决方案 1.使用openJDK或者非oracle的JDK建议 2.添加证书
// hutool的aes报错可以打开下面那段代码
// SecureUtil.disableBouncyCastle();
AES aes = new AES(passwordSignKey.getBytes(StandardCharsets.UTF_8));
String decryptStr;
String decryptPassword;
try {
decryptStr = aes.decryptStr(data);
decryptPassword = decryptStr.substring(13);
} catch (Exception e) {
logger.error("Exception:", e);
throw new YamiShopBindException("AES解密错误", e);
}
return decryptPassword;
}
}

@ -0,0 +1,63 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.security.common.util;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yami.shop.security.common.bo.UserInfoInTokenBO;
/**
* 线
* `TransmittableThreadLocal` 线便
*
*
* @author FrozenWatermelon
* @date 2020/7/16
*/
public class AuthUserContext {
// 使用 `TransmittableThreadLocal` 来创建一个线程本地变量,用于存储 `UserInfoInTokenBO` 类型的用户信息对象。
// `TransmittableThreadLocal` 相较于普通的 `ThreadLocal` 具有能在线程间传递数据的优势,
// 适合在多线程环境下(比如异步调用、线程池场景等)保证用户信息的正确传递和共享。
private static final ThreadLocal<UserInfoInTokenBO> USER_INFO_IN_TOKEN_HOLDER = new TransmittableThreadLocal<>();
/**
* 线
* `USER_INFO_IN_TOKEN_HOLDER` `get` `UserInfoInTokenBO`
* 线 `null`
*
* @return 线 `UserInfoInTokenBO` `null`
*/
public static UserInfoInTokenBO get() {
return USER_INFO_IN_TOKEN_HOLDER.get();
}
/**
* 线线
* `USER_INFO_IN_TOKEN_HOLDER` `set` `UserInfoInTokenBO`
* 线 `get`
*
* @param userInfoInTokenBo `UserInfoInTokenBO`
*/
public static void set(UserInfoInTokenBO userInfoInTokenBo) {
USER_INFO_IN_TOKEN_HOLDER.set(userInfoInTokenBo);
}
/**
* 线线
* 线 `USER_INFO_IN_TOKEN_HOLDER.get()` `null`
* `null` `USER_INFO_IN_TOKEN_HOLDER` `remove`
* 线
*/
public static void clean() {
if (USER_INFO_IN_TOKEN_HOLDER.get()!= null) {
USER_INFO_IN_TOKEN_HOLDER.remove();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.dao的包用于组织代码
package com.yami.shop.dao;
// 导入了MyBatis Plus框架中的BaseMapper接口用于提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入了Area实体类这个类代表了数据库中的一个表
import com.yami.shop.bean.model.Area;
/**
* MapperArea
*
* @author lanhai lanhai
*/
public interface AreaMapper extends BaseMapper<Area> {
// 这个接口继承了BaseMapper接口因此已经包含了基础的CRUD操作不需要额外定义方法
}

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.dao的包用于组织代码
package com.yami.shop.dao;
// 导入了MyBatis Plus框架中的BaseMapper接口用于提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入了AttachFile实体类这个类代表了数据库中的一个表
import com.yami.shop.bean.model.AttachFile;
/**
* MapperAttachFile
*
* @author lanhai lanhai
*/
public interface AttachFileMapper extends BaseMapper<AttachFile> {
// 这个接口继承了BaseMapper接口因此已经包含了基础的CRUD操作不需要额外定义方法
}

@ -0,0 +1,88 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.dao的包用于组织代码
package com.yami.shop.dao;
// 导入了MyBatis Plus框架中的BaseMapper接口用于提供基础的CRUD操作
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
// 导入了项目中定义的DTO和参数类
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.app.param.ShopCartParam;
// 导入了项目中的实体类
import com.yami.shop.bean.model.Basket;
// 导入了MyBatis的@Param注解用于在XML或注解中传递参数
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**
* MapperBasket
*
* @author lanhai lanhai
*/
public interface BasketMapper extends BaseMapper<Basket> {
/**
*
*
* @param userId id
* @return DTO
*/
List<ShopCartItemDto> getShopCartItems(@Param("userId") String userId);
/**
* idid
*
* @param userId id
* @param basketIds id
*/
void deleteShopCartItemsByBasketIds(@Param("userId") String userId, @Param("basketIds") List<Long> basketIds);
/**
*
*
* @param userId id
*/
void deleteAllShopCartItems(@Param("userId") String userId);
/**
*
*
* @param userId id
* @return DTO
*/
List<ShopCartItemDto> getShopCartExpiryItems(@Param("userId") String userId);
/**
*
*
* @param userId id
*/
void cleanExpiryProdList(@Param("userId") String userId);
/**
* id
*
* @param userId id
* @param basketIdShopCartParamMap id
*/
void updateDiscountItemId(@Param("userId")String userId, @Param("basketIdShopCartParamMap") Map<Long, ShopCartParam> basketIdShopCartParamMap);
/**
* idid
*
* @param prodId id
* @return id
*/
List<String> listUserIdByProdId(@Param("prodId")Long prodId);
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.model.Brand;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* @author lanhai
*/
public interface BrandMapper extends BaseMapper<Brand> {
/**
*
* @param brandName
* @return
*/
Brand getByBrandName(String brandName);
/**
* id
* @param categoryId id
* @return
*/
List<Brand> listByCategoryId(@Param("categoryId")Long categoryId);
}

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.model.CategoryBrand;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Mapper
*
* @author lanhai
*/
public interface CategoryBrandMapper extends BaseMapper<CategoryBrand> {
/**
*
* @param categoryId ID
* @param brandIds ID
*/
void insertCategoryBrand(@Param("categoryId") Long categoryId, @Param("brandIds") List<Long> brandIds);
/**
* ID
* @param categoryId ID
*/
void deleteByCategoryId(Long categoryId);
/**
* ID
* @param brandId ID
*/
void deleteByBrandId(Long brandId);
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.model.Category;
import java.util.List;
/**
* Mapper
*
* @author lanhai
*/
public interface CategoryMapper extends BaseMapper<Category> {
/**
* ID
* @param parentId ID
* @return
*/
List<Category> listByParentId(Long parentId);
/**
* ID
* @param shopId ID
* @return
*/
List<Category> tableCategory(Long shopId);
}

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.model.CategoryProp;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* Mapper
*
* @author lanhai
*/
public interface CategoryPropMapper extends BaseMapper<CategoryProp> {
/**
*
* @param categoryId ID
* @param propIds ID
*/
void insertCategoryProp(@Param("categoryId") Long categoryId, @Param("propIds") List<Long> propIds);
/**
* ID
* @param categoryId ID
*/
void deleteByCategoryId(Long categoryId);
/**
* ID
* @param propId ID
*/
void deleteByPropId(Long propId);
}

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义包名
package com.yami.shop.dao;
// 导入所需的类
import com.yami.shop.bean.model.Delivery;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* DeliveryMapper
* Mapper
*
* @author lanhai lanhai
*/
public interface DeliveryMapper extends BaseMapper<Delivery> {
// 这个接口继承了BaseMapper接口泛型参数为Delivery表示这个Mapper与Delivery实体类相关联
// 在这个接口中可以定义与Delivery表相关的数据库操作方法
// 由于继承了BaseMapper已经包含了很多基础的数据库操作方法如增删改查
// 如果需要额外的自定义方法可以在这个接口中声明然后在对应的XML文件或使用MyBatis Plus的注解方式实现
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.dto.HotSearchDto;
import com.yami.shop.bean.model.HotSearch;
import java.util.List;
/**
* HotSearchMapper 访MyBatis-PlusBaseMapper
* HotSearchCRUD
* ID
*
* @author lanhai
*/
public interface HotSearchMapper extends BaseMapper<HotSearch> {
/**
* getHotSearchDtoByShopId ID
* HotSearchDtoHotSearch
* API
*
* @param shopId
* @return HotSearchDto
*/
List<HotSearchDto> getHotSearchDtoByShopId(Long shopId);
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.bean.model.IndexImg;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* IndexImgMapper 访IndexImg
* MyBatis-PlusBaseMapperCRUD
* @author lanhai
*/
public interface IndexImgMapper extends BaseMapper<IndexImg> {
/**
* deleteIndexImgByIds ID
* @param ids ID
*/
void deleteIndexImgByIds(@Param("ids") Long[] ids);
/**
* listIndexImg
* @return IndexImg
*/
List<IndexImg> listIndexImg();
}

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.dao;
import com.yami.shop.bean.model.Message;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* MessageMapper 访Message
* MyBatis-PlusBaseMapperMessageCRUD
*
* XML使@Select
*
* @author lanhai
*/
public interface MessageMapper extends BaseMapper<Message> {
// 本接口当前没有定义额外的方法所有操作都依赖于BaseMapper提供的默认方法。
}

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.service的包用于组织代码
package com.yami.shop.service;
// 导入了MyBatis Plus框架中的分页插件Page类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入了MyBatis Plus框架中的IService接口用于提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入了Area实体类这个类代表了数据库中的一个表
import com.yami.shop.bean.model.Area;
import java.util.List;
import java.util.Map;
/**
* Area
*
* @author lgh on 2018/10/26 lgh20181026
*/
public interface AreaService extends IService<Area> {
/**
* id
*
* @param pid id
* @return Areaidpid
*/
List<Area> listByPid(Long pid);
/**
* id
*
* @param pid id
*/
void removeAreaCacheByParentId(Long pid);
}

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.service的包用于组织代码
package com.yami.shop.service;
// 导入了MyBatis Plus框架中的IService接口用于提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入了AttachFile实体类这个类代表了数据库中的一个表
import com.yami.shop.bean.model.AttachFile;
// 导入了Spring框架中的MultipartFile类用于处理上传的文件
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
/**
* AttachFile
*
* @author lanhai lanhai
* Created by lgh on 2018/07/27 lgh2018727
*/
public interface AttachFileService extends IService<AttachFile> {
/**
*
*
* @param file MultipartFile
* @throws IOException I/OIOException
* @return ID
*/
String uploadFile(MultipartFile file) throws IOException;
/**
*
*
* @param fileName
*/
void deleteFile(String fileName);
}

@ -0,0 +1,111 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.app.dto.ShopCartDto;
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.app.param.ChangeShopCartParam;
import com.yami.shop.bean.app.param.OrderItemParam;
import com.yami.shop.bean.app.param.ShopCartParam;
import com.yami.shop.bean.model.Basket;
import java.util.List;
import java.util.Map;
/**
* @author lgh on 2018/10/18.
*/
public interface BasketService extends IService<Basket> {
/**
* id
* @param userId id
* @param basketIds id
*/
void deleteShopCartItemsByBasketIds(String userId, List<Long> basketIds);
/**
*
* @param param
* @param userId
*/
void addShopCartItem(ChangeShopCartParam param, String userId);
/**
*
* @param basket
*/
void updateShopCartItem(Basket basket);
/**
*
* @param userId
*/
void deleteAllShopCartItems(String userId);
/**
* id
* @param userId id
* @return
*/
List<ShopCartItemDto> getShopCartItems(String userId);
/**
*
* @param userId id
* @return
*/
List<ShopCartItemDto> getShopCartExpiryItems(String userId);
/**
*
* @param userId id
*/
void cleanExpiryProdList(String userId);
/**
* id
* @param userId id
* @param basketIdShopCartParamMap map
*/
void updateBasketByShopCartParam(String userId, Map<Long, ShopCartParam> basketIdShopCartParamMap);
/**
*
* @param userId
*/
void removeShopCartItemsCacheByUserId(String userId);
/**
*
* @param prodId id
* @return id
*/
List<String> listUserIdByProdId(Long prodId);
/**
*
* @param shopCartItems
* @return
*/
List<ShopCartDto> getShopCarts(List<ShopCartItemDto> shopCartItems);
/**
*
* @param orderItem
* @param userId id
* @param basketId id
* @return
*/
List<ShopCartItemDto> getShopCartItemsByOrderItems(List<Long> basketId, OrderItemParam orderItem,String userId);
}

@ -0,0 +1,43 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.Brand;
import java.util.List;
/**
* @author lanhai
*/
public interface BrandService extends IService<Brand> {
/**
*
* @param brandName
* @return
*/
Brand getByBrandName(String brandName);
/**
*
* @param brandId
*/
void deleteByBrand(Long brandId);
/**
* id
* @param categoryId id
* @return
*/
List<Brand> listByCategoryId(Long categoryId);
}

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.CategoryBrand;
/**
* MyBatis PlusIService
*
* @author lanhai
*/
public interface CategoryBrandService extends IService<CategoryBrand> {
// 该接口目前为空继承了IService接口的所有方法可以添加自定义的方法
}

@ -0,0 +1,24 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.CategoryProp;
/**
* MyBatis PlusIService
*
* @author lanhai
*/
public interface CategoryPropService extends IService<CategoryProp> {
// 该接口目前为空继承了IService接口的所有方法可以添加自定义的方法
}

@ -0,0 +1,64 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.Category;
import java.util.List;
/**
* MyBatis PlusIService
*
* @author lanhai
*/
public interface CategoryService extends IService<Category> {
/**
* ID0
* @param parentId ID0
* @return
*/
List<Category> listByParentId(Long parentId);
/**
* IDseq
* @param shopId ID
* @return
*/
List<Category> tableCategory(Long shopId);
/**
*
* @param category
*/
void saveCategory(Category category);
/**
*
* @param category
*/
void updateCategory(Category category);
/**
*
* @param categoryId ID
*/
void deleteCategory(Long categoryId);
/**
* ID
* @param shopId ID
* @param grade
* @return
*/
List<Category> treeSelect(Long shopId, int grade);
}

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义包名表示这个类属于com.yami.shop.service包
package com.yami.shop.service;
// 导入MyBatis Plus框架中的IService接口这个接口提供了基本的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入项目中定义的Delivery实体类
import com.yami.shop.bean.model.Delivery;
/**
* DeliveryService
* Delivery
*
* @author lgh on 2018/11/26 lgh20181126
*/
public interface DeliveryService extends IService<Delivery> {
// 这个接口继承了IService接口泛型参数为Delivery表示这个服务接口与Delivery实体类相关联
// 在这个接口中可以定义与Delivery实体类相关的额外业务操作方法
// 由于继承了IService已经包含了很多基础的CRUD操作如增删改查
}

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义包名表示这个接口属于com.yami.shop.service包
package com.yami.shop.service;
// 导入MyBatis Plus框架中的IService接口提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入项目中定义的HotSearchDto数据传输对象
import com.yami.shop.bean.dto.HotSearchDto;
// 导入项目中定义的HotSearch实体类
import com.yami.shop.bean.model.HotSearch;
// 导入Java.util.List接口用于返回列表类型的数据
import java.util.List;
/**
* HotSearchService
* HotSearch
*
* @author lgh on 2019/03/27 lgh2019327
*/
public interface HotSearchService extends IService<HotSearch> {
/**
* ID
* LongIDHotSearchDto
*
* @param shopId ID
* @return ID
*/
List<HotSearchDto> getHotSearchDtoByShopId(Long shopId);
/**
* ID
* LongIDID
*
* @param shopId ID
*/
void removeHotSearchDtoCacheByShopId(Long shopId);
}

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义包名表示这个类属于com.yami.shop.service包
package com.yami.shop.service;
// 导入MyBatis Plus框架中的IService接口提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入项目中定义的IndexImg实体类
import com.yami.shop.bean.model.IndexImg;
// 导入Java.util.List接口用于返回列表类型的数据
import java.util.List;
/**
* IndexImgService
* IndexImg
*
* @author lgh on 2018/11/26 lgh20181126
*/
public interface IndexImgService extends IService<IndexImg> {
/**
* id
* LongID
*
* @param ids ID
*/
void deleteIndexImgByIds(Long[] ids);
/**
*
* List<IndexImg>
*
* @return
*/
List<IndexImg> listIndexImg();
/**
*
*
*/
void removeIndexImgCache();
}

@ -0,0 +1,29 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.model.Message;
/**
* MessageService Message
* MyBatis-PlusIServiceMessageCRUD
*
* ServiceImpl
*
*
* @author lgh on 2018/10/15.
*/
public interface MessageService extends IService<Message> {
// 本接口当前没有定义额外的方法所有操作都依赖于IService提供的默认方法。
// 如果需要增加特定的业务逻辑方法,可以在本接口中添加相应的方法签名。
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.app.dto.MyOrderDto;
import com.yami.shop.bean.model.Order;
/**
* MyOrderService Order
* MyBatis-PlusIServiceOrderCRUD
* ID
*
* @author lgh
*/
public interface MyOrderService extends IService<Order> {
/**
* pageMyOrderByUserIdAndStatus ID
*
* @param page
*
* @param userId
* @param status
* null
* @return IPage<MyOrderDto>
*
*/
IPage<MyOrderDto> pageMyOrderByUserIdAndStatus(Page<MyOrderDto> page, String userId, Integer status);
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义包名表示这个接口属于com.yami.shop.service包
package com.yami.shop.service;
// 导入MyBatis Plus框架中的分页插件的Page类
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
// 导入MyBatis Plus框架中的IService接口提供基础的CRUD操作
import com.baomidou.mybatisplus.extension.service.IService;
// 导入项目中定义的NoticeDto数据传输对象
import com.yami.shop.bean.app.dto.NoticeDto;
// 导入项目中定义的Notice实体类
import com.yami.shop.bean.model.Notice;
// 导入Java.util.List接口用于返回列表类型的数据
import java.util.List;
/**
* NoticeService
* Notice
*
* @author hzm hzm
* @date 2019-04-18 21:21:40 2019418
*/
public interface NoticeService extends IService<Notice> {
/**
*
* List<Notice>
*
* @return
*/
List<Notice> listNotice();
/**
*
*
*/
void removeNoticeList();
/**
*
* Page<NoticeDto>NoticeDto
*
* @param page
* @return DTO
*/
Page<NoticeDto> pageNotice(Page<NoticeDto> page);
/**
* id
* LongIDNotice
*
* @param noticeId ID
* @return
*/
Notice getNoticeById(Long noticeId);
/**
* id
* LongID
*
* @param noticeId ID
*/
void removeNoticeById(Long noticeId);
}

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.service.impl的包用于组织代码
package com.yami.shop.service.impl;
// 导入了MyBatis Plus框架中的LambdaQueryWrapper类用于构建条件查询
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
// 导入了MyBatis Plus框架中的ServiceImpl类用于提供基础的CRUD操作实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入了Area实体类这个类代表了数据库中的一个表
import com.yami.shop.bean.model.Area;
// 导入了AreaMapper接口用于数据库操作
import com.yami.shop.dao.AreaMapper;
// 导入了AreaService接口用于定义业务操作
import com.yami.shop.service.AreaService;
// 导入了Spring框架的注解用于注入依赖和定义服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* AreaService
*
* @author lgh on 2018/10/26 lgh20181026
*/
@Service
public class AreaServiceImpl extends ServiceImpl<AreaMapper, Area> implements AreaService {
// 使用Spring的@Autowired注解自动注入AreaMapper
@Autowired
private AreaMapper areaMapper;
/**
* id
* 使MyBatis PlusLambdaQueryWrapperidpid
* 使Spring@Cacheable"area"pid
*
* @param pid id
* @return Areaidpid
*/
@Override
@Cacheable(cacheNames = "area", key = "#pid")
public List<Area> listByPid(Long pid) {
return areaMapper.selectList(new LambdaQueryWrapper<Area>().eq(Area::getParentId, pid));
}
/**
* id
* 使Spring@CacheEvict"area"pid
*
* @param pid id
*/
@Override
@CacheEvict(cacheNames = "area", key = "#pid")
public void removeAreaCacheByParentId(Long pid) {
// 方法体为空清除缓存的逻辑由Spring Cache自动处理
}
}

@ -0,0 +1,134 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
// 定义了一个名为com.yami.shop.service.impl的包用于组织代码
package com.yami.shop.service.impl;
// 导入了Hutool工具类用于日期、文件和ID操作
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
// 导入了MyBatis Plus框架中的LambdaQueryWrapper类用于构建条件查询
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
// 导入了MyBatis Plus框架中的ServiceImpl类用于提供基础的CRUD操作实现
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
// 导入了七牛云存储相关类
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
// 导入了项目中的枚举、模型、工具类和配置类
import com.yami.shop.bean.enums.UploadType;
import com.yami.shop.bean.model.AttachFile;
import com.yami.shop.common.bean.Qiniu;
import com.yami.shop.common.util.ImgUploadUtil;
import com.yami.shop.common.util.Json;
import com.yami.shop.dao.AttachFileMapper;
import com.yami.shop.service.AttachFileService;
// 导入了Spring框架的注解用于注入依赖和定义服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
// 导入了Spring框架的MultipartFile类用于处理上传的文件
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;
/**
* AttachFileService
*
* @author lanhai lanhai
*/
@Service
public class AttachFileServiceImpl extends ServiceImpl<AttachFileMapper, AttachFile> implements AttachFileService {
// 使用Spring的@Autowired注解自动注入AttachFileMapper
@Autowired
private AttachFileMapper attachFileMapper;
// 使用Spring的@Autowired注解自动注入七牛云的UploadManager
@Autowired
private UploadManager uploadManager;
// 使用Spring的@Autowired注解自动注入七牛云的BucketManager
@Autowired
private BucketManager bucketManager;
// 使用Spring的@Autowired注解自动注入七牛云的配置信息
@Autowired
private Qiniu qiniu;
// 使用Spring的@Autowired注解自动注入七牛云的认证工具
@Autowired
private Auth auth;
// 使用Spring的@Autowired注解自动注入图片上传工具类
@Autowired
private ImgUploadUtil imgUploadUtil;
// 定义了一个常量,用于格式化文件名
public final static String NORM_MONTH_PATTERN = "yyyy/MM/";
/**
*
* 使Spring@Transactional
*
* @param file MultipartFile
* @throws IOException I/OIOException
* @return ID
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String uploadFile(MultipartFile file) throws IOException {
// 获取文件扩展名
String extName = FileUtil.extName(file.getOriginalFilename());
// 格式化文件名包含日期和UUID
String fileName = DateUtil.format(new Date(), NORM_MONTH_PATTERN) + IdUtil.simpleUUID() + "." + extName;
// 创建AttachFile对象并设置属性
AttachFile attachFile = new AttachFile();
attachFile.setFilePath(fileName);
attachFile.setFileSize(file.getBytes().length);
attachFile.setFileType(extName);
attachFile.setUploadTime(new Date());
// 判断上传类型,如果是本地上传,则保存文件信息到数据库,并上传文件到本地
if (Objects.equals(imgUploadUtil.getUploadType(), 1)) {
attachFileMapper.insert(attachFile);
return imgUploadUtil.upload(file, fileName);
} else {
// 如果是七牛云上传,则获取上传凭证,并上传文件到七牛云
String upToken = auth.uploadToken(qiniu.getBucket(), fileName);
Response response = uploadManager.put(file.getBytes(), fileName, upToken);
// 解析上传响应
Json.parseObject(response.bodyString(), DefaultPutRet.class);
return fileName;
}
}
/**
*
*
* @param fileName
*/
@Override
public void deleteFile(String fileName){
// 根据文件名删除数据库中的记录
attachFileMapper.delete(new LambdaQueryWrapper<AttachFile>().eq(AttachFile::getFilePath, fileName));
try {
// 判断上传类型,如果是本地上传,则删除本地文件
if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.LOCAL.value())) {
imgUploadUtil.delete(fileName);
} else if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.QINIU.value())) {
// 如果是七牛云上传,则删除七牛云上的文件
bucketManager.delete(qiniu.getBucket(), fileName);
}
} catch (QiniuException e) {
// 如果删除失败,抛出运行时异常
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,246 @@
/*
* Copyright (c) 2018-2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.service.impl;
// 导入所需的类和包
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.yami.shop.bean.app.dto.*;
import com.yami.shop.bean.app.param.ChangeShopCartParam;
import com.yami.shop.bean.app.param.OrderItemParam;
import com.yami.shop.bean.app.param.ShopCartParam;
import com.yami.shop.bean.event.ShopCartEvent;
import com.yami.shop.bean.model.Basket;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.model.Sku;
import com.yami.shop.common.util.Arith;
import com.yami.shop.common.util.CacheManagerUtil;
import com.yami.shop.dao.BasketMapper;
import com.yami.shop.service.BasketService;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.ShopDetailService;
import com.yami.shop.service.SkuService;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
*
*
* @author lgh on 2018/10/18.
*/
@Service
@AllArgsConstructor
public class BasketServiceImpl extends ServiceImpl<BasketMapper, Basket> implements BasketService {
// 注入的组件
private final BasketMapper basketMapper;
private final CacheManagerUtil cacheManagerUtil;
private final ApplicationContext applicationContext;
private final SkuService skuService;
private final ShopDetailService shopDetailService;
private final ProductService productService;
/**
* ID
* @param userId ID
* @param basketIds ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void deleteShopCartItemsByBasketIds(String userId, List<Long> basketIds) {
basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds);
}
/**
*
* @param param
* @param userId ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void addShopCartItem(ChangeShopCartParam param, String userId) {
Basket basket = new Basket();
basket.setBasketCount(param.getCount());
basket.setBasketDate(new Date());
basket.setProdId(param.getProdId());
basket.setShopId(param.getShopId());
basket.setUserId(userId);
basket.setSkuId(param.getSkuId());
basket.setDistributionCardNo(param.getDistributionCardNo());
basketMapper.insert(basket);
}
/**
*
* @param basket
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#basket.userId")
public void updateShopCartItem(Basket basket) {
basketMapper.updateById(basket);
}
/**
*
* @param userId ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void deleteAllShopCartItems(String userId) {
basketMapper.deleteAllShopCartItems(userId);
}
/**
*
* @param userId ID
* @return
*/
@Override
public List<ShopCartItemDto> getShopCartItems(String userId) {
// 从缓存中获取购物车商品列表,如果缓存中没有则从数据库中获取
List<ShopCartItemDto> shopCartItemDtoList = cacheManagerUtil.getCache("ShopCartItems", userId);
if (shopCartItemDtoList != null) {
return shopCartItemDtoList;
}
shopCartItemDtoList = basketMapper.getShopCartItems(userId);
for (ShopCartItemDto shopCartItemDto : shopCartItemDtoList) {
shopCartItemDto.setProductTotalAmount(Arith.mul(shopCartItemDto.getProdCount(), shopCartItemDto.getPrice()));
}
cacheManagerUtil.putCache("ShopCartItems", userId, shopCartItemDtoList);
return shopCartItemDtoList;
}
/**
*
* @param userId ID
* @return
*/
@Override
public List<ShopCartItemDto> getShopCartExpiryItems(String userId) {
return basketMapper.getShopCartExpiryItems(userId);
}
/**
*
* @param userId ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void cleanExpiryProdList(String userId) {
basketMapper.cleanExpiryProdList(userId);
}
/**
*
* @param userId ID
* @param basketIdShopCartParamMap ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void updateBasketByShopCartParam(String userId, Map<Long, ShopCartParam> basketIdShopCartParamMap) {
basketMapper.updateDiscountItemId(userId, basketIdShopCartParamMap);
}
/**
* ID
* @param userId ID
*/
@Override
@CacheEvict(cacheNames = "ShopCartItems", key = "#userId")
public void removeShopCartItemsCacheByUserId(String userId) {
// 该方法体为空,可能是待实现的方法
}
/**
* IDID
* @param prodId ID
* @return ID
*/
@Override
public List<String> listUserIdByProdId(Long prodId) {
return basketMapper.listUserIdByProdId(prodId);
}
/**
*
* @param shopCartItems
* @return
*/
@Override
public List<ShopCartDto> getShopCarts(List<ShopCartItemDto> shopCartItems) {
// 根据店铺ID分组购物车商品项
Map<Long, List<ShopCartItemDto>> shopCartMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
// 构建购物车信息列表
List<ShopCartDto> shopCartDtos = Lists.newArrayList();
for (Long shopId : shopCartMap.keySet()) {
// 获取店铺的所有商品项
List<ShopCartItemDto> shopCartItemDtoList = shopCartMap.get(shopId);
// 构建每个店铺的购物车信息
ShopCartDto shopCart = new ShopCartDto();
shopCart.setShopId(shopId);
shopCart.setShopName(shopCartItemDtoList.get(0).getShopName());
// 发布购物车事件
applicationContext.publishEvent(new ShopCartEvent(shopCart, shopCartItemDtoList));
shopCartDtos.add(shopCart);
}
return shopCartDtos;
}
/**
* ID
* @param basketId ID
* @param orderItem
* @param userId ID
* @return
*/
@Override
public List<ShopCartItemDto> getShopCartItemsByOrderItems(List<Long> basketId, OrderItemParam orderItem, String userId) {
if (orderItem == null && CollectionUtil.isEmpty(basketId)) {
return Collections.emptyList();
}
// 当立即购买时,没有提交的订单是没有购物车信息的
if (CollectionUtil.isEmpty(basketId) && orderItem != null) {
Sku sku = skuService.getSkuBySkuId(orderItem.getSkuId());
if (sku == null) {
throw new RuntimeException("订单包含无法识别的商品");
}
Product prod = productService.getProductByProdId(orderItem.getProdId());
if (prod == null) {
throw new RuntimeException("订单包含无法识别的商品");
}
// 构建购物车商品项
ShopCartItemDto shopCartItemDto = new ShopCartItemDto();
shopCartItemDto.setBasketId(-1L);
shopCartItemDto.setSkuId(orderItem.getSkuId());
shopCartItemDto.setProdCount(orderItem.getProdCount());
shopCartItemDto.setProdId(orderItem.getProdId());
shopCartItemDto.setSkuName(sku.getSkuName());
shopCartItemDto.setPic(StrUtil.isBlank(sku.getPic()) ? prod.getPic() : sku.getPic());
shopCartItemDto.setProdName(sku.getProdName());
shopCartItemDto.set
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save