sharp-common集成Spring Web

sharp-common 集成Spring Web主要体现在以下3个方面

  • 通过注解 @RestControllerAdvice 封装异常异常 ApiExceptionHandler
  • 注入国际化的工具类 MessageUtils
  • 封装 Result 返回对象

集成

  • 添加包扫描,将 ApiExceptionHandlerMessageUtils纳入Spring上下文,@EnableResultWrapped 自动包裹 Result 对象。
    @Configuration
    @Import({ApiExceptionHandler.class, MessageUtils.class})
    @EnableResultWrapped
    public class MvcConfig implements WebMvcConfigurer {}
  • 国际化文件
# messages_en_US.properties
BEAN_VALIDATE_ERROR=Params validate error:{0}
MAX_TRY_LOGIN_ERROR=The account failed to log in {0} times, please wait {1} minutes and try again

# messages_zh_CN.properties
BEAN_VALIDATE_ERROR=参数验证错误:{0}
MAX_TRY_LOGIN_ERROR=该账号{0}次登录失败后,被锁定{1}分钟
  • 添加业务异常枚举
    @Getter
    @ToString
    public enum ExceptionCode {

        BEAN_VALIDATE_ERROR(30002, "BEAN_VALIDATE_ERROR"),
        PRICE_CRAWLER_ERROR(40004, "网站价格获取失败");

        private int code;

        private String msg;

        ExceptionCode(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }

        public ExceptionResult<String> result() {
            return new ExceptionResult<>(getCode(), getMsg());
        }
    }

BEAN_VALIDATE_ERROR 在国际化文件中进行了语言维护。

测试

@RestController
@RequestMapping("test")
public class TestController {

    @GetMapping("1")
    public Result test1() {
        return ResultUtils.success(MessageUtils.getMessage("MAX_TRY_LOGIN_ERROR", new Object[] {5, 20}));
    }

    @GetMapping("2")
    public Result test2() {
        int a = 1 / 0;
        return ResultUtils.success();
    }

    @GetMapping("3")
    public Result test3() {
        return ResultUtils.fail();
    }

    @GetMapping("4")
    public Result test4() {
        throw new BizException(ExceptionCode.FORMULA_ERROR.result());
    }

    @GetMapping("5")
    public Result test5() {
        throw new BizException(ExceptionCode.BEAN_VALIDATE_ERROR.result(), new Object[] {"姓名不能为空"});
    }

    @GetMapping("6")
    public int test5() {
        return 1;
    }

    @GetMapping("7")
    @UnWrapped
    public int test7() {
        return 7;
    }
}

注解 @UnWrapped 表示不需要被 Result 对象包裹。

sharp-sms使用指北

简介

sharp-sms 是基于阿里云的短信配置包依赖。

如何使用

添加 pom 依赖

<dependency>
    <groupId>com.rick.sms</groupId>
    <artifactId>sharp-sms</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

阿里云配置

application.yml

ali:
  access-key:
    id: LTAI4FkT323Aq4WU144WgZpwRA6D
    secret: U9eehrtFFsLt3SsP8E232TBhiyNh8Ghwvh

需要在阿里云的短信平台去获取

测试

@Autowired
private Sender sender;

@Test
public void testSend() {
    Map<String, String> params = new HashMap<>(3);
    params.put("name", "hello");
    params.put("value", "world");
    sender.send("18888888888", "XX公司", "SMS_202587654", params);
}

「XX公司」表示 签名,「SMS_202587654」表示 模版。 需要在阿里云的短信平台去配置。params参数就来自于 模版 中的变量。

sharp-common之JsonUtils

简介

Jackson是基于Java平台的一套数据处理工具,被称为”最好的Java Json解析器”。它可以使我们高效、简便的处理json字符串。

添加 pom 依赖

<dependency>
    <groupId>com.rick.common</groupId>
    <artifactId>sharp-common</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

对象转json字符串

public static String toJson(Object obj) throws IOException;
@Test
public void testToJson() throws IOException {
    Dept dept = new Dept();
    dept.setId(1L);
    dept.setName("Dev");
    dept.setParentId(2L);
    String json = JsonUtils.toJson(dept);
    System.out.println(json);
}

控制台输出

{"id":1,"name":"Dev","parentId":2}

json字符串转普通对象

public static <T> T toObject(String json, Class<T> clazz) throws IOException;

范型对象

@Test
public void testToPOJO() throws IOException {
    Dept dept = JsonUtils.toObject("{\"id\":1,\"name\":\"Dev\",\"parentId\":2}", Dept.class);
    Assert.assertEquals(1L, dept.getId().longValue());
}

Map对象

@Test
public void testToMap() throws IOException {
    Map dept = JsonUtils.toObject("{\"id\":1,\"name\":\"Dev\",\"parentId\":2}", Map.class);
    // dept.get("id") Integer类型
    Assert.assertEquals(1, dept.get("id"));
}

List对象

@Test
public void testToList() throws IOException {
    List list = JsonUtils.toObject("[{\"id\":1,\"name\":\"Dev\",\"parentId\":2}]", List.class);
    // dept.get("id") Integer类型
    Assert.assertEquals(1, ((Map)list.get(0)).get("id"));
}

json字符串转List

利用TypeReference,构造类型引用对象

@Test
public void testToListWithGenerics1() throws IOException {
    TypeReference<List<Dept>> typeRef = new TypeReference<List<Dept>>() {};
    List<Dept> list = JsonUtils.toObject("[{\"id\":1,\"name\":\"Dev\",\"parentId\":2}]", typeRef);
    Assert.assertEquals(1L, list.get(0).getId().longValue());
}

底层依赖利用JavaType实现

public static <T> List<T> toList(String json, Class<T> clazz) throws IOException;
@Test
public void testToListWithGenerics2() throws IOException {
    List<Dept> list = JsonUtils.toList("[{\"id\":1,\"name\":\"Dev\",\"parentId\":2}]", Dept.class);
    Assert.assertEquals(1L, list.get(0).getId().longValue());
}

json字符串转JsonNode

JsonNode后 ,操作对象比较转 Map List 对象更加的方便

public static JsonNode toJsonNode(String json) throws IOException;

list的Json字符串转JsonNode

@Test
public void testListStringToJsonNode() throws IOException {
    JsonNode jsonNode = JsonUtils.toJsonNode("[{\"id\":1,\"name\":\"Dev\",\"parentId\":2}]");
    Assert.assertEquals(1L, jsonNode.get(0).get("id").longValue());
}

对象的Json字符串转JsonNode

@Test
public void testObjectStringToJsonNode() throws IOException {
    JsonNode jsonNode = JsonUtils.toJsonNode("{\"id\":1,\"name\":\"Dev\",\"parentId\":2}");
    Assert.assertEquals(1L, jsonNode.get("id").longValue());
}

对象转JsonNode

@Test
public void testObjectToJsonNode() throws IOException {
    Dept dept = new Dept();
    dept.setId(1L);
    dept.setName("Dev");
    dept.setParentId(2L);
    JsonNode jsonNode = JsonUtils.toJsonNode(dept);
    Assert.assertEquals(1L, jsonNode.get("id").longValue());
}

Map转JsonNode

@Test
public void testMapToJsonNode() {
    String id = "1";
    Map<String, String> params = new HashMap<>();
    params.put("id", id);
    JsonNode jsonNode = JsonUtils.toJsonNode(params);
    Assert.assertEquals(1L, jsonNode.get("id").asLong());
}

sharp-meta操作指北

添加依赖

<dependency>
    <groupId>com.rick.meta</groupId>
    <artifactId>sharp-meta</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

创建脚本

create table sys_dict
(
type varchar(32) not null,
name varchar(32) not null,
label varchar(32) not null,
sort int null,
primary key (type, name)
)
charset=utf8mb4;

create table sys_property
(
name varchar(32) not null
primary key,
value varchar(255) not null
)
charset=utf8mb4;

INSERT INTO sys_dict (type, name, label, sort) VALUES ('sex', 'F', '女', 1);
INSERT INTO sys_dict (type, name, label, sort) VALUES ('sex', 'M', '男', 0);

INSERT INTO project_demo.sys_property (name, value) VALUES ('hello', 'world');

读取配置文件

application-dict.yml

dict:
  items:
    - type: user
      sql: "select username, name from sys_user order by id asc"
    - type: grade
      map: {g1: "一年级", g2: "二年级"}

创建测试

@RunWith(SpringRunner.class)
@SpringBootTest
public class MetaTest {

    @Autowired
    private DictService dictService;

    @Autowired
    private PropertyService propertyService;

    @Test
    public void testList() {
        List<DictDO> sexList = dictService.getDictByType("sex");
        Assert.assertEquals(2, sexList.size());
    }

    @Test
    public void testGetOne() {
        DictDO dictDO = dictService.getDictByTypeAndName("sex", "F").get();
        Assert.assertEquals("女", dictDO.getLabel());
    }

    @Test
    public void testDictYml() {
        Assert.assertEquals(2, dictService.getDictByType("grade").size());
        Assert.assertEquals("一年级",dictService.getDictByTypeAndName("grade", "g1").get().getLabel());
        Assert.assertEquals("Rick",dictService.getDictByTypeAndName("user", "jkxyx205").get().getLabel());
        Assert.assertEquals("男",dictService.getDictByTypeAndName("sex", "M").get().getLabel());
    }

    @Test
    public void testGetProperty() {
        String property = propertyService.getProperty("hello");
        Assert.assertEquals("world", property);
    }

    @Test
    public void testSetProperty() {
        propertyService.setProperty("gg", "dd");
    }
}

sharp-excel使用指北:高级部分(二)

简介

sharp-excel 是一个操作Excel、Word工具的java实现。依赖apache的 POI。目前只开发了Excel的部分,Word部分还没有实现。
pom.xml

<dependency>
    <groupId>com.rick.office</groupId>
    <artifactId>sharp-excel</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>compile</scope>
</dependency>

设计的理念是定(x, y)坐标,然后写数据

方法测试

在指定的坐标位置,写入数据

单元格数据

@Test
public void testCell() throws IOException {
    File file = new File("/Users/rick/Documents/3.xlsx");

    ExcelWriter excelWriter = new ExcelWriter();

    excelWriter.writeCell(new ExcelCell(4, 5, "hello"));

    excelWriter.toFile(new FileOutputStream(file));
}

在坐标(4, 5)的单元格写入「hello」
http://xhope.top/wp-content/uploads/2021/09/0.png

行数据

@Test
public void writeRow() throws IOException {
    File file = new File("/Users/rick/Documents/9.xlsx");
    ExcelWriter excelWriter = new ExcelWriter();
    excelWriter.writeRow(new ExcelRow(2,2, new Object[] {1, 2, 3, 4, 5, "hello", true}));
    excelWriter.toFile(new FileOutputStream(file));
}

从坐标是(2, 2) 的位置开始,写入「一行」数据:1, 2, 3, 4, 5, “hello”, true
http://xhope.top/wp-content/uploads/2021/09/2.png

列数据

@Test
public void writeColumn() throws IOException {
    File file = new File("/Users/rick/Documents/8.xlsx");
    ExcelWriter excelWriter = new ExcelWriter();
    excelWriter.writeColumn(new ExcelColumn(2,2, new Object[] {1, 2, 3, 4, 5, "hello", true}));
    excelWriter.toFile(new FileOutputStream(file));
}

从坐标是(2, 2) 的位置开始,写入「一列」数据:1, 2, 3, 4, 5, “hello”, true
http://xhope.top/wp-content/uploads/2021/09/1.png

合并单元格

@Test
public void testWriteMerge() throws IOException {
    File file = new File("/Users/rick/Documents/0.xlsx");

    ExcelWriter excelWriter = new ExcelWriter();

    ExcelCell cell = new ExcelCell(2, 3, "hello");

    cell.setRowSpan(3);
    cell.setColSpan(2);

    excelWriter.writeCell(cell);

    excelWriter.toFile(new FileOutputStream(file));
}

在坐标(2, 3)的单元格写入「hello」,占3行2列
http://xhope.top/wp-content/uploads/2021/09/merge.png

设置样式

单元格

@Test
public void testWriteCellWithStyle() throws IOException {
    File file = new File("/Users/rick/Documents/3.xlsx");

    ExcelWriter excelWriter = new ExcelWriter();

    ExcelCell cell = new ExcelCell(2,1, "hello");

    cell.setHeightInPoints(50f);
    cell.setStyle(createStyle(excelWriter.getBook()));

    excelWriter.writeCell(cell);

    excelWriter.toFile(new FileOutputStream(file));
}

http://xhope.top/wp-content/uploads/2021/09/s1.png

@Test
public void testWriteRowWithStyle() throws IOException {
    File file = new File("/Users/rick/Documents/3.xlsx");

    ExcelWriter excelWriter = new ExcelWriter();

    excelWriter.getActiveSheet().setColumnWidth(2, 5600);

    ExcelRow row = new ExcelRow(2,2, new Object[] {1.2d, 23, "3", true, LocalDate.now()});
    row.setStyle(createStyle(excelWriter.getBook()));
    excelWriter.writeRow(row);

    excelWriter.toFile(new FileOutputStream(file));
}

http://xhope.top/wp-content/uploads/2021/09/s2.png

同行设置

复杂例子

@Test
public void testComplex() throws IOException {
    File file = new File("/Users/rick/Documents/7.xlsx");
    ExcelWriter excelWriter = new ExcelWriter();

    ExcelCell cell1 = new ExcelCell(1,1, "国内仓");
    cell1.setColSpan(3);
    cell1.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell2 = new ExcelCell(4,1, "香港仓");
    cell2.setRowSpan(2);
    cell2.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell3 = new ExcelCell(5,1, "香港直运仓");
    cell3.setRowSpan(2);
    cell3.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell4 = new ExcelCell(6,1, "供应链未完成数量");
    cell4.setRowSpan(2);
    cell4.setStyle(createStyle(excelWriter.getBook()));

    ExcelRow row1 = new ExcelRow(1, 2, new Object[] {"苏州成品仓", "苏州样品仓", "深圳成品仓"});
    row1.setStyle(createStyle(excelWriter.getBook()));

    ExcelRow row2 = new ExcelRow(1, 3, new Object[] {"50", "0", "1"});
    row2.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell5 = new ExcelCell(1,4, "国内仓锁货数量");
    cell5.setColSpan(3);
    cell5.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell6 = new ExcelCell(1,5, "9");
    cell6.setColSpan(3);
    cell6.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell7 = new ExcelCell(4,3, "29");
    cell7.setRowSpan(3);
    cell7.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell8 = new ExcelCell(5,3, "39");
    cell8.setRowSpan(3);
    cell8.setStyle(createStyle(excelWriter.getBook()));

    ExcelCell cell9 = new ExcelCell(6,3, 88);
    cell9.setRowSpan(3);
    cell9.setStyle(createStyle(excelWriter.getBook()));

    ExcelColumn column = new ExcelColumn(7, 1, new Object[] {"新增", "2", "23", 23, 34.2f});
    column.setStyle(createStyle(excelWriter.getBook()));

    excelWriter.writeCell(cell1);
    excelWriter.writeCell(cell2);
    excelWriter.writeCell(cell3);
    excelWriter.writeCell(cell4);
    excelWriter.writeCell(cell5);
    excelWriter.writeCell(cell6);
    excelWriter.writeCell(cell7);
    excelWriter.writeCell(cell8);
    excelWriter.writeCell(cell9);
    excelWriter.writeRow(row1);
    excelWriter.writeRow(row2);
    excelWriter.writeColumn(column);

    excelWriter.getActiveSheet().setColumnWidth(0, 5600);
    excelWriter.getActiveSheet().setColumnWidth(1, 5600);
    excelWriter.getActiveSheet().setColumnWidth(2, 5600);
    excelWriter.getActiveSheet().setColumnWidth(3, 5600);
    excelWriter.getActiveSheet().setColumnWidth(4, 5600);
    excelWriter.getActiveSheet().setColumnWidth(5, 5600);
    excelWriter.getActiveSheet().setColumnWidth(6, 5600);

    excelWriter.toFile(new FileOutputStream(file));

}

private XSSFCellStyle createStyle(XSSFWorkbook book) {

    XSSFCellStyle cellStyle = book.createCellStyle();
    // 定义颜色
    XSSFColor color = new XSSFColor(Color.black, new DefaultIndexedColorMap());

    // 设置边框(合并这个不生效) 需要单独在CellRangeAddress设置
    cellStyle.setBorderLeft(BorderStyle.THIN);
    cellStyle.setBorderRight(BorderStyle.THIN);
    cellStyle.setBorderTop(BorderStyle.THIN);
    cellStyle.setBorderBottom(BorderStyle.THIN);

    cellStyle.setBorderColor(XSSFCellBorder.BorderSide.LEFT, color);
    cellStyle.setBorderColor(XSSFCellBorder.BorderSide.RIGHT, color);
    cellStyle.setBorderColor(XSSFCellBorder.BorderSide.TOP, color);
    cellStyle.setBorderColor(XSSFCellBorder.BorderSide.BOTTOM, color);

    // 水平居中
    cellStyle.setAlignment(HorizontalAlignment.CENTER);
    // 垂直居中
    cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

    return cellStyle;
}

http://xhope.top/wp-content/uploads/2021/09/complex.png

POI原生实现例子

@Test
public void testNative() throws IOException {
    File file = new File("/Users/rick/Documents/2.xlsx");

    XSSFWorkbook book = new XSSFWorkbook();
    XSSFSheet sheet = book.createSheet("sheet-rick");

    // 设置第一列的框
    sheet.setColumnWidth(0, 3766);

    XSSFRow row = sheet.createRow(0);


    // 设置高度
    row.setHeightInPoints(24);
//        row.setRowStyle();
    XSSFCell cell = row.createCell(1);
    cell.setCellValue("hello");
//        cell.setCellStyle();


    XSSFCellStyle cellStyle = book.createCellStyle();

    // 定义颜色
    XSSFColor color = new XSSFColor(java.awt.Color.BLUE, new DefaultIndexedColorMap());
    XSSFColor color2 = new XSSFColor(Color.RED, new DefaultIndexedColorMap());
    XSSFColor color3 = new XSSFColor(Color.GREEN, new DefaultIndexedColorMap());

    // 填充色
    cellStyle.setFillForegroundColor(color2);
    cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

    // 文字色
    XSSFFont font = book.createFont();
    font.setColor(color);
    cellStyle.setFont(font);

    // 设置边框(合并这个不生效) 需要单独在CellRangeAddress设置
//        cellStyle.setBorderBottom(BorderStyle.MEDIUM);
//        cellStyle.setBorderColor(XSSFCellBorder.BorderSide.BOTTOM, color3);

    // 水平居中
    cellStyle.setAlignment(HorizontalAlignment.CENTER);
    // 垂直居中
    cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

    // 设置样式
    cell.setCellStyle(cellStyle);

    // 合并单元格
    CellRangeAddress region = new CellRangeAddress(0, 1, 1, 2);
    sheet.addMergedRegion(region);

    //
    System.out.println(cellStyle.getBorderBottom() == BorderStyle.NONE);
    setRegionStyle(sheet, region);

    book.write(new FileOutputStream(file));
    book.close();

}

/**
 * 设置边框
 //  * @param region
 */
 private void setRegionStyle(XSSFSheet sheet, CellRangeAddress region) {
    //全部完成之后
    XSSFRow xrow = (XSSFRow) CellUtil.getRow(region.getFirstRow(), sheet);
    XSSFCell xccell = (XSSFCell) CellUtil.getCell(xrow, region.getFirstColumn());

    XSSFCellStyle style = xccell.getCellStyle();

    for (int i = region.getFirstRow(); i <= region.getLastRow(); i++) {
        XSSFRow row = (XSSFRow) CellUtil.getRow(i, sheet);
        for (int j = region.getFirstColumn(); j <= region.getLastColumn(); j++) {
            XSSFCell cell = (XSSFCell) CellUtil.getCell(row, j);
            cell.setCellStyle(style);
            System.out.println("-----");
        }
    }
 }