您好,欢迎来到化拓教育网。
搜索
您的当前位置:首页基于若依框架完成的尚庭公寓项目

基于若依框架完成的尚庭公寓项目

来源:化拓教育网

   

        尚庭公寓项目,即房屋租赁项目,是b站尚硅发布的一套全新的视频,对于java基础较薄弱的同学有一定帮助。我在这个项目基础上做出一些大量的改动,采用了比较流行的若依框架。目前只是完成了管理员部分的。在完成这些部分的基础上对我所遇到的问题进行如下记录。

  一、测试技术的修改

        该原有项目原本是采用Knife4j方法测试,但若依框架所提供的测试技术是Swagger,因此在Swagger的基础上进行修改

        (1)首先是注解的修改。在controller中,需要把每个@Tag标签换成@Api,把每个@Operation换成@ApiOperation。例如

@Tag(name = "房间属性管理")

换成

@Api(tags = "房间属性管理")

,然后

@Operation(summary = "新增或更新属性名称")

换成

@ApiOperation("新增或更新属性名称")

,然后对于vo类和实体类,@Schema换成@ApiModel,例如:

@Schema(description = "房间基本属性表")

换成

@ApiModel(description = "房间基本属性表")

,这样用swagger测试的每个api或者实体vo类的名字描述才能正常显示出来。

(2)原有的swagger配置代码:

/**
 * Swagger2的接口配置
 *
 * @author ruoyi
 */
@Configuration
public class SwaggerConfig
{
    /** 系统基础配置 */
    @Autowired
    private RuoYiConfig ruoyiConfig;

    /** 是否开启swagger */
    @Value("${swagger.enabled}")
    private boolean enabled;

    /** 设置请求的统一前缀 */
    @Value("${swagger.pathMapping}")
    private String pathMapping;

    /**
     * 创建API
     */
    @Bean
    public Docket createRestApi()
    {
        return new Docket(DocumentationType.OAS_30)
                // 是否启用Swagger
                .enable(enabled)
                // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
                .apiInfo(apiInfo())
                // 设置哪些接口暴露给Swagger展示
                .select()
                // 扫描所有有注解的api,用这种方式更灵活
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                // 扫描指定包中的swagger注解
                // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger"))
                // 扫描所有 .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()
                /* 设置安全模式,swagger可以设置访问token */
                .securitySchemes(securitySchemes())
                .securityContexts(securityContexts())
                .pathMapping(pathMapping);
    }

    /**
     * 安全模式,这里指定token通过Authorization头请求头传递
     */
    private List<SecurityScheme> securitySchemes()
    {
        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
        apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
        return apiKeyList;
    }

    /**
     * 安全上下文
     */
    private List<SecurityContext> securityContexts()
    {
        List<SecurityContext> securityContexts = new ArrayList<>();
        securityContexts.add(
                SecurityContext.builder()
                        .securityReferences(defaultAuth())
                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
                        .build());
        return securityContexts;
    }

    /**
     * 默认的安全上引用
     */
    private List<SecurityReference> defaultAuth()
    {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
        return securityReferences;
    }

    /**
     * 添加摘要信息
     */
    private ApiInfo apiInfo()
    {
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                // 设置标题
                .title("标题:房屋易租系统_接口文档")
                // 描述
                .description("描述:用于管理房屋的信息.")
                // 作者信息
                .contact(new Contact(ruoyiConfig.getName(), null, null))
                // 版本
                .version("版本号:" + ruoyiConfig.getVersion())
                .build();
    }
}

但是对于登录后,因为要携带一个jwt生成的token值,而原有的这个配置并不能满足我的需求,所以需要:

A.在securitySchemes方法添加access-token配置:

private List<SecurityScheme> securitySchemes()
    {
        List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
        apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
        apiKeyList.add(new ApiKey("access-token", "access-token", In.HEADER.toValue()));
        return apiKeyList;
    }

B.在defaultAuth()方法添加 access-token 的安全引用:

private List<SecurityReference> defaultAuth()
    {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        List<SecurityReference> securityReferences = new ArrayList<>();
        securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
        // 添加 access-token 的安全引用
        securityReferences.add(new SecurityReference("access-token", authorizationScopes));
        return securityReferences;
    }

浏览器输入,然后右边会有个Authorize,点击后:

下面刚好多出了一个access-token,这样当登录成功后拿到了token值填入到下面点击Authorize即可,否则测试每一个接口都会提示未登录错误。

        这里再提一下上面的Authorization,我在ruoyi-framework\src\main\java\com\ruoyi\framework\config\SecurityConfig.java中第117行进行修改:

 .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**","/admin/**","/app/**").permitAll()

添加"/admin/**","/app/**上去,这样就不会报401错。

二、枚举类型的修改

        在项目提供的源代码上,用swagger调试含有枚举类型的api方法时,类型转换有问题,这里我举一个有关变迁类型的列表查看功能,代码如下:

@ApiOperation("(根据类型)查询标签列表")
    @GetMapping("list")
    public Result<List<LabelInfo>> labelList(@RequestParam(required = false) ItemType type) {
        LambdaQueryWrapper<LabelInfo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(type != null, LabelInfo::getType, type);
        List<LabelInfo> list = service.list(queryWrapper);
        return Result.ok(list);
    }

其对应的枚举类型有房间和公寓:

public enum ItemType implements BaseEnum {

    APARTMENT(1, "公寓"),
    ROOM(2, "房间");

    @EnumValue
    @JsonValue
    private Integer code;
    private String name;

    ItemType(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getName() {
        return name;
    }

}

测试的结果:

这里面不难看出,这里面传的值就有问题,应该是1或2,而不是APARTMENT或ROOM,可能是Jackson 默认会优先使用枚举的 name() 方法(即 APARTMENTROOM)。当你将 @JsonValue 移到 getCode() 方法上时,就明确告诉 Jackson 使用 getCode() 方法的返回值(12)作为枚举的 JSON 表现形式。解决方法是将@JsonValue放在public Integer getCode()上面:

@JsonValue
    @Override
    public Integer getCode() {
        return this.code;
    }

但这样还没结束,虽然传的值变为了1或2,但测试的时候data还是为空:

再看控制台,传入的却依然是name值:

为了解决这一问题,对于这个问题,为了确保枚举对象的解析正确,需要自己另写fromCode方法,来获取枚举实例,该枚举类所有代码如下:

public enum ItemType implements BaseEnum {

    APARTMENT(1, "公寓"),
    ROOM(2, "房间");

    @EnumValue

    private Integer code;
    private String name;

    ItemType(Integer code, String name) {
        this.code = code;
        this.name = name;
    }
    @JsonValue
    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getName() {
        return name;
    }

    public static ItemType fromCode(Integer code) {
        for (ItemType itemType : ItemType.values()) {
            if (itemType.getCode().equals(code)) {
                return itemType;
            }
        }
        throw new IllegalArgumentException("Unknown enum code: " + code);
    }
}

这个fromCode方法根据给定的 code 值找到对应的 ItemType 枚举实例。如果找到匹配的枚举,则返回相应的枚举对象,否则抛出异常。接下来就要自定义 MyBatis 类型处理器 (TypeHandler) 和配置:

public class BaseStatusTypeHandler extends BaseTypeHandler<BaseStatus> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, BaseStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }

    @Override
    public BaseStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        return BaseStatus.fromCode(code);
    }

    @Override
    public BaseStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int code = rs.getInt(columnIndex);
        return BaseStatus.fromCode(code);
    }

    @Override
    public BaseStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int code = cs.getInt(columnIndex);
        return BaseStatus.fromCode(code);
    }

}

最后注册自定义的类型处理器即可:

@Configuration
public class MyBatisConfig {
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.getTypeHandlerRegistry().register(ItemType.class, ItemTypeHandler.class);
    }
}

再重新测试,然后便可查到相关数据,接下来对于其他的枚举类型也是如此方法。

三、前端省市区的修改

        在后端代码无误的情况下,当前后端联调后,在前端中的公寓添加或修改信息中,省市区这三个字段传值后点击确认,所显示的数据为空且数据库对应的省市区信息也为空,对应的vue文件是\src\views\apartmentManagement\apartmentManagement\components\addOrEditApartment.vue可以看到省市区对应的@change方法写的有问题,修改如下:

// 省份改变回调
const provinceChangeCallback = async () => {
  let provinceId = formData.value.provinceId
  if (provinceId) {
    resetCity()
    resetDistrict()
    await getCityListHandle(provinceId)
    const province = areaInfo.provinceList.find(item => item.id === provinceId);
    formData.value.provinceName = province ? province.name : '';
  }
}
// 省份清除回调
const provinceClearCallback = () => {
  formData.value.provinceId = ''
  resetCity()
  resetDistrict()
}
// 城市改变回调
const cityChangeCallback = async () => {
  let cityId = formData.value.cityId
  if (cityId) {
    resetDistrict()
    await getDistrictListHandle(cityId)
     // 更新城市名称
     const city = areaInfo.cityList.find(item => item.id === cityId);
    formData.value.cityName = city ? city.name : '';
  }
}
// 城市清除回调
const cityClearCallback = () => {
  console.log('清空城市')
  formData.value.cityId = ''
  resetDistrict()
}
// 区域改变回调
const districtChangeCallback = async () => {
  console.log('区域改变')
   // 更新区域名称
   let districtId = formData.value.districtId;
   const district = areaInfo.districtList.find(item => item.id === districtId);
  formData.value.districtName = district ? district.name : '';
  console.log("区名:",formData.value.districtName);
  
}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo9.cn 版权所有 赣ICP备2023008801号-1

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务