SpringBoot项目--电脑商城【新增收货地址】

1.新增收货地址

t_address

sql 复制代码
CREATE TABLE t_address (
	aid INT AUTO_INCREMENT COMMENT '收货地址id',
	uid INT COMMENT '归属的用户id',
	`name` VARCHAR(20) COMMENT '收货人姓名',
	province_name VARCHAR(15) COMMENT '省-名称',
	province_code CHAR(6) COMMENT '省-行政代号',
	city_name VARCHAR(15) COMMENT '市-名称',
	city_code CHAR(6) COMMENT '市-行政代号',
	area_name VARCHAR(15) COMMENT '区-名称',
	area_code CHAR(6) COMMENT '区-行政代号',
	zip CHAR(6) COMMENT '邮政编码',
	address VARCHAR(50) COMMENT '详细地址',
	phone VARCHAR(20) COMMENT '手机',
	tel VARCHAR(20) COMMENT '固话',
	tag VARCHAR(6) COMMENT '标签',
	is_default INT COMMENT '是否默认:0-不默认,1-默认',
	created_user VARCHAR(20) COMMENT '创建人',
	created_time DATETIME COMMENT '创建时间',
	modified_user VARCHAR(20) COMMENT '修改人',
	modified_time DATETIME COMMENT '修改时间',
	PRIMARY KEY (aid)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

注意name是关键字,所以需要用``

2.创建收货地址的实体类

在entity包下创建实体类Address继承BaseEntity类

java 复制代码
/**
 * 收货地址的实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address extends BaseEntity {
    private Integer aid;//收货地址id
    private Integer uid;//归属用户id
    private String name;//收货人姓名
    private String provinceName;//省
    private String provinceCode;//省行政代号
    private String cityName;//市名
    private String cityCode;//市行政代号
    private String areaName;//区名
    private String areaCode;//区行政代号
    private String zip;//邮政编码
    private String address;//详细地址
    private String phone;//手机
    private String tel;//固话
    private String tag;//标签
    private Integer isDefault;//是否默认  0-不默认,1-默认
}

3.持久层[Mapper]

1 各功能的开发顺序

当前收货地址功能模块:

  • 第一个页面:列表的展示,修改,删除,设置默认
  • 第二个页面:新增收货地址

开发顺序:新增收货地址->列表的展示->设置默认收货地址->删除收货地址->修改收货地址

2 需要规划要执行的sql语句

1.新增收货地址对应的是插入语句:

java 复制代码
insert into t_address (aid以外的所有字段) values (字段值)
  1. 大部分平台都会规定一个用户的收货地址数量,这里规定最多20个.那么在插入用户新的地址之前就要先做查询操作.如果查询到的是刚好20,这并不是一个java语法的异常,可以认为是业务控制的异常,这个异常随后在service抛,在controller捕获
java 复制代码
select count(*) from t_address where uid=?

2.设置接口和抽象方法

创建接口AddressMapper,在这个接口中定义上面两个SQL语句抽象方法定义

java 复制代码
//收货地址持久层接口
public interface AddressMapper {
    /**
     * 插入用户的收货地址数据
     *
     * @param address 收货地址
     * @return 受影响的行数
     */
    Integer insertAddress(Address address);

    /**
     * 获取收货地址的数量(不能超过20),根据用户的uid
     *
     * @param uid 用户uid
     * @return 收货地址数量
     */
    Integer countAddress(Integer uid);
}

3.编写映射

1.在mapper标签中配置Address类属性与数据库中表的字段映射

XML 复制代码
    <resultMap id="AddressEntityMap" type="com.example.mycomputerstore.entity.Address">
        <id     column="aid"            property="aid"/>
        <result column="province_code"  property="provinceCode"/>
        <result column="province_name"  property="provinceName"/>
        <result column="city_code"      property="cityCode"/>
        <result column="city_name"      property="cityName"/>
        <result column="area_code"      property="areaCode"/>
        <result column="area_name"      property="areaName"/>
        <result column="is_default"     property="isDefault"/>
        <result column="created_user"   property="createdUser"/>
        <result column="created_time"   property="createdTime"/>
        <result column="modified_user"  property="modifiedUser"/>
        <result column="modified_time"  property="modifiedTime"/>
    </resultMap>

判断该映射是否配置成功:按着ctrl并点击type="com.cy.store.entity.Address"中的Address,如果能跳转到Address类说明映射成功

2.在AddressMapper.xml中配置以上两个抽象方法的映射

java 复制代码
    <insert id = "insertAddress" useGeneratedKeys="true" keyProperty="aid">
        INSERT INTO t_address (
            uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,
            address, phone, tel, tag, is_default, created_user, created_time, modified_user, modified_time)
        VALUES (
                   #{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode},
                   #{areaName}, #{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag},
                   #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})
    </insert>

    <select id="countAddress" resultType="java.lang.Integer">
        SELECT count(*) FROM t_address WHERE uid = #{uid}
    </select>

4.单元测试

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressMapperTests {

    @Autowired
    private AddressMapper addressMapper;

    @Test
    public void insert() {
        Address address = new Address();
        address.setUid(11);
        address.setPhone("133336");
        address.setName("女朋友");
        addressMapper.insert(address);
    }

    @Test
    public void countByUid() {
        Integer count = addressMapper.countByUid(11);
        System.out.println(count);
    }
}

4.业务层[service]

1. 规划异常

  • 插入数据时用户不存在(被管理员误删等等),抛UsernameNotFoundException异常(已经有了,不需要重复创建)
  • 当用户插入的地址是第一条时,需要将当前地址作为默认收货地址
  • 实现办法:如果查询到统计总数为0则将当前地址的is_default值设置为1
  • 如果查询的结果>=20,这时需要抛出业务控制的异常AddressCountLimitException
java 复制代码
/**收货地址总数超出限制的异常(20条)*/
public class AddressCountLimitException extends ServiceException {
    /**重写ServiceException的所有构造方法*/
}
  • 插入数据时产生未知的异常InsertException(已经有了,不需要重复创建)

2 设计接口和抽象方法及实现

1.创建一个IAddressService接口,在接口中定义业务的抽象方法

因为mapper层接口该功能模块定义了两个抽象方法,所以就要在service层接口该功能模块也定义两个抽象方法?不是这样的,要看mapper层的这两个方法是依赖关系还是独立关系,如果某一个抽象方法依赖于另一个抽象方法,那就需要在业务层将这两个方法整合到一个方法中.一句话来说就是:一个功能模块可能需要多条sql语句

java 复制代码
//收货地址业务层接口
public interface IAddressService {
    /**
     *这三个参数的由来:
     * 1.首先肯定要有address
     * 2.业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
     * 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层
     * 3.业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
     * 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层
     * 注意:> 可以用HttpSession session代替Integer uid, String username,但
     * 这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个
     * 类中并让IAddressServiceImp实现类继承该类,这样就需要微调一下代码逻辑,太麻
     * 烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就
     * 把session对象定义封装在控制层中,不需要在业务层中额外处理以降低耦合
     */
    void addAddress(Integer uid, String username, Address address);
}

方法addNewAddress中三个参数的由来:

  • 首先肯定要有address
  • 业务层需要根据uid查询该用户收货地址总数及新建地址时给字段uid赋值
  • 但新建收货地址的表单中并没有哪个控件让输入用户uid,所以需要控制层将uid传给业务层并在业务层封装到address对象中
  • 业务层在创建/修改收货地址时需要同时修改数据库中创建人/修改人的字段
  • 但新建收货地址的表单中并没有哪个控件让输入用户username,所以需要控制层将username传给业务层并在业务层封装到address对象中

可以用HttpSession session代替Integer uid, String username,但这样写的话就需要把BaseController类下获取uid,username的方法重新封装到一个类中并让AddressServiceImpl实现类继承该类,这样就需要微调一下代码逻辑,太麻烦,并且,最好每一层只处理该层需要做的事情,session对象是控制层传递的,所以就把session对象定义封装在控制层中,不需要在业务层中额外处理,这样可以降低耦合

2.创建一个AddressServiceImpl类实现接口中抽象方法

java 复制代码
@Service
public class IAddressServiceImpl implements IAddressService {

    @Autowired
    private AddressMapper addressMapper;


    //在添加用户的收货地址的业务层依赖于DistrictService的业务层接口
    @Autowired
    private IDistrictService districtService;

    /**
     * 为了方便日后修改最大收货地址数量,可以在配置文件
     * application.properties中定义user.address.max-count=20
     */
    @Value("${user.address.max-count}")
    private Integer MaxAddress ;


    @Override
    public void addAddress(Integer uid, String username, Address address) {
        //查询收货地址信息是否大于20
        Integer count = addressMapper.countAddress(uid);
        if(count >= MaxAddress) {
            throw new AddressCountLimitException("收货地址不能超过20条");
        }

        //设置信息
        address.setUid(uid);
        Integer isDelete = count == 0 ? 1 : 0;//1表示默认收货地址,0反之
        address.setIsDefault(isDelete);
        address.setCreatedTime(new Date());
        address.setCreatedUser(username);
        address.setModifiedTime(new Date());
        address.setModifiedUser(username);

        //对address对象中的数据进行补全:省市区
        String provinceName = districtService.getNameByCode(address.getProvinceCode());
        String cityName = districtService.getNameByCode(address.getCityCode());
        String areaName = districtService.getNameByCode(address.getAreaCode());
        address.setProvinceName(provinceName);
        address.setCityName(cityName);
        address.setAreaName(areaName);


        //插入收货地址
        Integer row = addressMapper.insertAddress(address);
        if(row != 1) {
            throw new InsertException("未知错误 在 新建收货地址");
        }
    }
}

别忘了在配置文件application.properties中定义user.address.max-count=20

3 单元测试

在test下的service文件夹下创建AddressServiceTests测试类

java 复制代码
@SpringBootTest
@RunWith(SpringRunner.class)
public class AddressServiceTests {
    @Autowired
    private IAddressService addressService;

    @Test
    public void addNewAddress() {
        Address address = new Address();
        address.setPhone("175726");
        address.setName("男朋友");
        addressService.addNewAddress(11,"mxy",address);
    }
}

5.控制层[Controller]

1 处理异常

义务层抛出了收货地址总数超出上限的异常,在BaseController中进行捕获处理

java 复制代码
else if (e instanceof AddressCountLimitException) {
    result.setState(4003);
    result.setMessage("用户的收货地址超出上限的异常");
}

2 设计请求

  • /addresses/add_new_address
  • post
  • Address address,HttpSession session
  • JsonResult<Void>

3. 处理请求

在controller包下创建AddressController并继承BaseController,该类用来处理用户收货地址的请求和响应

java 复制代码
/**
 * 用户收货地址
 */
@RestController
@RequestMapping("/address")
public class AddressController extends BaseController {

    @Autowired
    private IAddressService addressService;


    /**
     * 新增用户收货地址
     *
     * @param address
     * @param session:这里使用session 是为了获取用户的uid和username
     * @return
     */
    @PostMapping("/add_new_address")
    public JsonResult<Void> addAddress(Address address, HttpSession session) {
        //先获取用户uid和username
        Integer uid = getuidFromSession(session);
        String username = getUsernameFromSession(session);
        addressService.addAddress(uid, username, address);
        return new JsonResult<>(OK);
    }
}

6.前端页面

javascript 复制代码
		$("#btn-add-new-address").click(function (){
			$.ajax({
				url:"/address/add_new_address",
				type:"POST",
				data:$("#form-add-new-address").serialize(),
				dataType:"JSON",
				success(e){
					if(e.state==200){
						alert("新增收货地址成功")
					}else{
						alert("新增收货地址失败")
					}
				},
				error(xhr){
					alert("新增收货地址产生未知的异常"+xhr.status)
				}
			})
		})
相关推荐
Dcs16 分钟前
VSCode等多款主流 IDE 爆出安全漏洞!插件“伪装认证”可执行恶意命令!
java
保持学习ing21 分钟前
day1--项目搭建and内容管理模块
java·数据库·后端·docker·虚拟机
京东云开发者33 分钟前
Java的SPI机制详解
java
超级小忍1 小时前
服务端向客户端主动推送数据的几种方法(Spring Boot 环境)
java·spring boot·后端
程序无bug1 小时前
Spring IoC注解式开发无敌详细(细节丰富)
java·后端
小莫分享1 小时前
Java Lombok 入门
java
程序无bug1 小时前
Spring 对于事务上的应用的详细说明
java·后端
食亨技术团队1 小时前
被忽略的 SAAS 生命线:操作日志有多重要
java·后端
宇钶宇夕1 小时前
EPLAN 电气制图:建立自己的部件库,添加部件-加SQL Server安装教程(三)上
运维·服务器·数据库·程序人生·自动化
苦学编程的谢1 小时前
Maven
java·maven·intellij-idea