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


    1.新增收货地址

    t_address

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

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

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

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

    1. /**
    2. * 收货地址的实体类
    3. */
    4. @Data
    5. @AllArgsConstructor
    6. @NoArgsConstructor
    7. public class Address extends BaseEntity {
    8. private Integer aid;//收货地址id
    9. private Integer uid;//归属用户id
    10. private String name;//收货人姓名
    11. private String provinceName;//省
    12. private String provinceCode;//省行政代号
    13. private String cityName;//市名
    14. private String cityCode;//市行政代号
    15. private String areaName;//区名
    16. private String areaCode;//区行政代号
    17. private String zip;//邮政编码
    18. private String address;//详细地址
    19. private String phone;//手机
    20. private String tel;//固话
    21. private String tag;//标签
    22. private Integer isDefault;//是否默认 0-不默认,1-默认
    23. }

    3.持久层[Mapper]

    1 各功能的开发顺序

    当前收货地址功能模块:

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

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

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

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

    insert into t_address (aid以外的所有字段) values (字段值)
    

    2. 大部分平台都会规定一个用户的收货地址数量,这里规定最多20个.那么在插入用户新的地址之前就要先做查询操作.如果查询到的是刚好20,这并不是一个java语法的异常,可以认为是业务控制的异常,这个异常随后在service抛,在controller捕获

    select count(*) from t_address where uid=?
    

    2.设置接口和抽象方法

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

    1. //收货地址持久层接口
    2. public interface AddressMapper {
    3. /**
    4. * 插入用户的收货地址数据
    5. *
    6. * @param address 收货地址
    7. * @return 受影响的行数
    8. */
    9. Integer insertAddress(Address address);
    10. /**
    11. * 获取收货地址的数量(不能超过20),根据用户的uid
    12. *
    13. * @param uid 用户uid
    14. * @return 收货地址数量
    15. */
    16. Integer countAddress(Integer uid);
    17. }

    3.编写映射

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

    1. <resultMap id="AddressEntityMap" type="com.example.mycomputerstore.entity.Address">
    2. <id column="aid" property="aid"/>
    3. <result column="province_code" property="provinceCode"/>
    4. <result column="province_name" property="provinceName"/>
    5. <result column="city_code" property="cityCode"/>
    6. <result column="city_name" property="cityName"/>
    7. <result column="area_code" property="areaCode"/>
    8. <result column="area_name" property="areaName"/>
    9. <result column="is_default" property="isDefault"/>
    10. <result column="created_user" property="createdUser"/>
    11. <result column="created_time" property="createdTime"/>
    12. <result column="modified_user" property="modifiedUser"/>
    13. <result column="modified_time" property="modifiedTime"/>
    14. resultMap>

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

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

    1. <insert id = "insertAddress" useGeneratedKeys="true" keyProperty="aid">
    2. INSERT INTO t_address (
    3. uid, name, province_name, province_code, city_name, city_code, area_name, area_code, zip,
    4. address, phone, tel, tag, is_default, created_user, created_time, modified_user, modified_time)
    5. VALUES (
    6. #{uid}, #{name}, #{provinceName}, #{provinceCode}, #{cityName}, #{cityCode},
    7. #{areaName}, #{areaCode}, #{zip}, #{address}, #{phone}, #{tel}, #{tag},
    8. #{isDefault}, #{createdUser},#{createdTime}, #{modifiedUser}, #{modifiedTime})

    4.单元测试

    1. @SpringBootTest
    2. @RunWith(SpringRunner.class)
    3. public class AddressMapperTests {
    4. @Autowired
    5. private AddressMapper addressMapper;
    6. @Test
    7. public void insert() {
    8. Address address = new Address();
    9. address.setUid(11);
    10. address.setPhone("133336");
    11. address.setName("女朋友");
    12. addressMapper.insert(address);
    13. }
    14. @Test
    15. public void countByUid() {
    16. Integer count = addressMapper.countByUid(11);
    17. System.out.println(count);
    18. }
    19. }

    4.业务层[service]

    1. 规划异常

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

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

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

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

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

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

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

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

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

    1. @Service
    2. public class IAddressServiceImpl implements IAddressService {
    3. @Autowired
    4. private AddressMapper addressMapper;
    5. //在添加用户的收货地址的业务层依赖于DistrictService的业务层接口
    6. @Autowired
    7. private IDistrictService districtService;
    8. /**
    9. * 为了方便日后修改最大收货地址数量,可以在配置文件
    10. * application.properties中定义user.address.max-count=20
    11. */
    12. @Value("${user.address.max-count}")
    13. private Integer MaxAddress ;
    14. @Override
    15. public void addAddress(Integer uid, String username, Address address) {
    16. //查询收货地址信息是否大于20
    17. Integer count = addressMapper.countAddress(uid);
    18. if(count >= MaxAddress) {
    19. throw new AddressCountLimitException("收货地址不能超过20条");
    20. }
    21. //设置信息
    22. address.setUid(uid);
    23. Integer isDelete = count == 0 ? 1 : 0;//1表示默认收货地址,0反之
    24. address.setIsDefault(isDelete);
    25. address.setCreatedTime(new Date());
    26. address.setCreatedUser(username);
    27. address.setModifiedTime(new Date());
    28. address.setModifiedUser(username);
    29. //对address对象中的数据进行补全:省市区
    30. String provinceName = districtService.getNameByCode(address.getProvinceCode());
    31. String cityName = districtService.getNameByCode(address.getCityCode());
    32. String areaName = districtService.getNameByCode(address.getAreaCode());
    33. address.setProvinceName(provinceName);
    34. address.setCityName(cityName);
    35. address.setAreaName(areaName);
    36. //插入收货地址
    37. Integer row = addressMapper.insertAddress(address);
    38. if(row != 1) {
    39. throw new InsertException("未知错误 在 新建收货地址");
    40. }
    41. }
    42. }

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

    3 单元测试

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

    1. @SpringBootTest
    2. @RunWith(SpringRunner.class)
    3. public class AddressServiceTests {
    4. @Autowired
    5. private IAddressService addressService;
    6. @Test
    7. public void addNewAddress() {
    8. Address address = new Address();
    9. address.setPhone("175726");
    10. address.setName("男朋友");
    11. addressService.addNewAddress(11,"mxy",address);
    12. }
    13. }

    5.控制层[Controller]

    1 处理异常

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

    1. else if (e instanceof AddressCountLimitException) {
    2. result.setState(4003);
    3. result.setMessage("用户的收货地址超出上限的异常");
    4. }

    2 设计请求

    • /addresses/add_new_address
    • post
    • Address address,HttpSession session
    • JsonResult

    3. 处理请求

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

    1. /**
    2. * 用户收货地址
    3. */
    4. @RestController
    5. @RequestMapping("/address")
    6. public class AddressController extends BaseController {
    7. @Autowired
    8. private IAddressService addressService;
    9. /**
    10. * 新增用户收货地址
    11. *
    12. * @param address
    13. * @param session:这里使用session 是为了获取用户的uid和username
    14. * @return
    15. */
    16. @PostMapping("/add_new_address")
    17. public JsonResult addAddress(Address address, HttpSession session) {
    18. //先获取用户uid和username
    19. Integer uid = getuidFromSession(session);
    20. String username = getUsernameFromSession(session);
    21. addressService.addAddress(uid, username, address);
    22. return new JsonResult<>(OK);
    23. }
    24. }

    6.前端页面

    1. $("#btn-add-new-address").click(function (){
    2. $.ajax({
    3. url:"/address/add_new_address",
    4. type:"POST",
    5. data:$("#form-add-new-address").serialize(),
    6. dataType:"JSON",
    7. success(e){
    8. if(e.state==200){
    9. alert("新增收货地址成功")
    10. }else{
    11. alert("新增收货地址失败")
    12. }
    13. },
    14. error(xhr){
    15. alert("新增收货地址产生未知的异常"+xhr.status)
    16. }
    17. })
    18. })
  • 相关阅读:
    Java手写队列和案例拓展
    大数据之Hudi数据湖_基本概念_文件布局_文件管理 & 基本概念_索引_的原理_索引选项_全局索引与非全局索引---大数据之Hudi数据湖工作笔记0007
    react redux demo
    Golang — template
    Unity IL2CPP 游戏分析入门
    如何低门槛开发有趣实用的ZigBee产品?
    【redis持久化模式】两种持久化方式、概念、原理、如何使用、优劣势_Redis05
    基于Socket实现网络编程
    【总结】各层的校验和的特点
    什么是js中的作用域
  • 原文地址:https://blog.csdn.net/m0_63077733/article/details/132719632