实例:sharp-*完成可配置的增删改查

人员信息表

数据库设计

表名:t_user

字段名 描述 字段类型 java 字段 UI 控件
name 姓名 varchar(32) String input[text]
gender 性别 varchar(32) Enum(by code) radio
age 年龄 int Integer input[number]
birthday 出生时间 datetime LocalDate input[date]
mobile 手机号码 varchar String text
email 邮箱 varchar String input[email]
native_place 籍贯 varchar(32) String select
hobby 兴趣爱好 varchar(32) Enum(by name) checkbox
marriage 婚否 bit String checkbox
introduce 自我介绍 text String textarea
attachment 附件 text List file
school_experience 学习经历 text List table
status 用户状态 varchar(32) Enum(by order) select

常规代码

User.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Table(value = "t_user", comment = "人员信息表")
public class User extends BaseEntity {

    @Column(comment = "姓名", nullable = false)
    private String name;

    @Column(comment = "性别", nullable = false)
    private GenderEnum gender;

    @Column(comment = "年龄", nullable = false)
    private Integer age;

    @Column(comment = "出生时间")
    private LocalDate birthday;

    @Column(comment = "手机号码", nullable = false)
    private String mobile;

    @Column(comment = "邮箱")
    private String email;

    @Column(comment = "籍贯", nullable = false)
    private String nativePlace;

    @Column(comment = "兴趣爱好", value = "hobby", nullable = false)
    private List<HobbyEnum> hobbyList;

    @Column(comment = "婚否", nullable = false)
    private Boolean marriage;

    @Column(comment = "自我介绍", columnDefinition = "text", nullable = false)
    private String introduce;

    @Column(comment = "附件", columnDefinition = "text", value = "attachment", nullable = false)
    private List<Map<String, Object>> attachmentList;

    @Column(comment = "学习经历", columnDefinition = "text", value = "school_experience")
    private List<List<String>> schoolExperienceList;

    @Column(comment = "用户状态")
    private StatusEnum status;

    @AllArgsConstructor
    @Getter
    public enum GenderEnum {
        UNKNOWN(0, "Unknown"),
        MALE(1, "Male"),
        FEMALE(2, "Female");
        private static final Map<Integer, GenderEnum> codeMap = new HashMap<>();
        static {
            for (GenderEnum e : values()) {
                codeMap.put(e.code, e);
            }
        }
        private final int code;
        private final String label;
        @JsonValue
        public int getCode() {
            return this.code;
        }
        public static GenderEnum valueOfCode(int code) {
            return codeMap.get(code);
        }
    }

    @Getter
    public enum StatusEnum {
        LOCKED, NORMAL
    }

    @AllArgsConstructor
    @Getter
    public enum HobbyEnum {
        BASKETBALL("篮球"),
        FOOTBALL("羽毛球"),
        BADMINTON("羽毛球"),
        VOLLEYBALL("排球");

        @JsonValue
        public String getCode() {
            return this.name();
        }
        private final String label;
        public static HobbyEnum valueOfCode(String code) {
            return valueOf(code);
        }
    }
}

执行 tableGenerator.createTable(User.class) 生成表。

create table t_user
(
    id bigint not null comment '主键'
        primary key,
    name varchar(32) not null comment '姓名',
    gender varchar(16) not null comment '性别',
    age int not null comment '年龄',
    birthday date null comment '出生时间',
    mobile varchar(32) not null comment '手机号码',
    email varchar(32) null comment '邮箱',
    native_place varchar(32) not null comment '籍贯',
    hobby json not null comment '兴趣爱好',
    marriage bit not null comment '婚否',
    introduce text null,
    attachment text null,
    school_experience text null,
    status varchar(16) null comment '用户状态'
)
comment '人员信息表';

DAO

UserDAO.java

@Repository
public class UserDAO extends EntityDAOImpl<User, Long> {

}

UserFormAdvice

@Component("userFormAdvice")
public class UserFormAdvice implements FormAdvice {

    @Override
    public void beforeInstanceHandle(FormBO form, Long instanceId, Map<String, Object> values) {
        // checkbox List => boolean
        if (CollectionUtils.isEmpty((Collection<?>) values.get("marriage"))) {
            values.put("marriage", false);
        } else {
            values.put("marriage", true);
        }
    }

    @Override
    public void afterInstanceHandle(FormBO form, Long instanceId, Map<String, Object> values) {

    }
}

创建UI控件

@Test
  public void testAddConfigurerToForm() {
      // 设计控件
      List<CpnConfigurer> cpnConfigurerList = createCpnConfigurerList();
      cpnConfigurerService.saveOrUpdate(cpnConfigurerList);

      // 创建表
      Form form = formService.save(Form.builder()
              .code("t_user")
              .tableName("t_user")
              .name("人员信息表")
              .repositoryName("userDAO")
              .formAdviceName("userFormAdvice")
              .storageStrategy(Form.StorageStrategyEnum.CREATE_TABLE)
              .build());

      // 关联关系
      formCpnService.saveOrUpdateByConfigIds(form.getId(), cpnConfigurerList.stream().map(CpnConfigurer::getId).collect(Collectors.toList()));
  }

  public List<CpnConfigurer> createCpnConfigurerList() {
      // 文本校验器
      Set<Validator> textValidatorList = Sets.newHashSetWithExpectedSize(2);
      textValidatorList.add(new Required(true));
      textValidatorList.add(new Length(16));

      Set<Validator> requiredValidatorList = Sets.newHashSetWithExpectedSize(1);
      requiredValidatorList.add(new Required(true));

      CpnConfigurer nameCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.TEXT)
              .name("name")
              .label("姓名")
              .placeholder("请输入姓名")
              .defaultValue("Rick.Xu")
              .validatorList(textValidatorList)
              .build();

      CpnConfigurer genderCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.RADIO)
              .name("gender")
              .label("性别")
              .options(Arrays.asList(new CpnConfigurer.CpnOption("0", "未知"), new CpnConfigurer.CpnOption("1", "男"), new CpnConfigurer.CpnOption("2", "女")))
              .defaultValue("1")
              .validatorList(requiredValidatorList)
              .build();

      Set<Validator> ageValidatorList = Sets.newHashSetWithExpectedSize(1);
      ageValidatorList.add(new Size(18, 100));
      ageValidatorList.add(new Required(true));
      CpnConfigurer ageCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.NUMBER_TEXT)
              .name("age")
              .label("年龄")
              .placeholder("请输入年龄")
              .defaultValue("28")
              .validatorList(ageValidatorList)
              .build();

      CpnConfigurer birthdayCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.DATE)
              .name("birthday")
              .label("出生时间")
              .placeholder("请输入出生时间")
              .defaultValue("1989-12-11")
              .build();

      CpnConfigurer mobileCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.MOBILE)
              .name("mobile")
              .label("手机号码")
              .placeholder("请输入手机号码")
              .defaultValue("18898765543")
              .validatorList(requiredValidatorList)
              .build();

      CpnConfigurer emailCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.EMAIL)
              .name("email")
              .label("邮箱")
              .placeholder("请输入邮箱")
              .defaultValue("jkxyx@163.com")
              .build();

      CpnConfigurer nativePlaceCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.SELECT)
              .name("native_place")
              .label("籍贯")
              .options(Arrays.asList(new CpnConfigurer.CpnOption("陕西"), new CpnConfigurer.CpnOption("江苏"), new CpnConfigurer.CpnOption("四川"), new CpnConfigurer.CpnOption("上海")))
              .defaultValue("江苏")
              .validatorList(requiredValidatorList)
              .build();

      CpnConfigurer hobbyCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.CHECKBOX)
              .name("hobby")
              .label("兴趣爱好")
              .options(Arrays.asList(new CpnConfigurer.CpnOption("BASKETBALL", "篮球"), new CpnConfigurer.CpnOption("FOOTBALL", "足球"), new CpnConfigurer.CpnOption("BADMINTON", "羽毛球"), new CpnConfigurer.CpnOption("VOLLEYBALL", "排球"))                 )
              .defaultValue("[\"BASKETBALL\", \"FOOTBALL\"]")
              .validatorList(requiredValidatorList)
              .build();

      CpnConfigurer marriageCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.CHECKBOX)
              .name("marriage")
              .label("婚否")
//                .options(Arrays.asList(new CpnConfigurer.CpnOption("true", "")) )
              .options(Arrays.asList(new CpnConfigurer.CpnOption("1", "")) )
              .defaultValue("true")
              .build();

      CpnConfigurer introduceCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.TEXTAREA)
              .name("introduce")
              .label("自我介绍")
              .placeholder("请输入自我介绍")
              .defaultValue("我叫Rick...")
              .validatorList(textValidatorList)
              .build();

      CpnConfigurer attachmentCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.FILE)
              .name("attachment")
              .label("附件")
              .validatorList(requiredValidatorList)
              .build();

      CpnConfigurer yearCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.TEXT)
              .name("school_year")
              .label("年份")
              .defaultValue("Rick.Xu")
              .validatorList(textValidatorList)
              .build();

      CpnConfigurer schoolNameCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.TEXT)
              .name("school_name")
              .label("学校名称")
              .defaultValue("Rick.Xu")
              .validatorList(textValidatorList)
              .build();

      CpnConfigurer schoolExperienceCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.TABLE)
              .name("school_experience")
              .label("学习经历")
              .additionalInfo(Params.builder()
                        .pv("columns", Arrays.asList(yearCpn, schoolNameCpn))
                      .build())
              .defaultValue("[[\"2021-2022\", \"四川大学\"], [\"2012-2015\", \"永安高中\"]]")
              .build();

      CpnConfigurer statusCpn = CpnConfigurer.builder()
              .cpnType(CpnTypeEnum.SELECT)
              .name("status")
              .label("用户状态")
              .options(Arrays.asList(new CpnConfigurer.CpnOption("LOCKED", "锁定"), new CpnConfigurer.CpnOption("NORMAL", "正常")))
              .defaultValue("LOCKED")
              .build();

      List<CpnConfigurer> cpnConfigurerList = Lists.newArrayList(nameCpn, genderCpn, ageCpn, birthdayCpn,
              mobileCpn, emailCpn, nativePlaceCpn, hobbyCpn, marriageCpn, introduceCpn, attachmentCpn, schoolExperienceCpn, statusCpn);
      return cpnConfigurerList;
  }

访问表单

新增 POST /{form_id}
修改 POST /{form_id}/{instance_id}
查找 GET /{form_id}/{instance_id}

http://xhope.top/wp-content/uploads/2023/03/from.png

创建报表

@Test
public void testAddReport() {
    reportService.saveOrUpdate(Report.builder()
            .id(668074769748373504L)
            .name("人员信息表")
            .querySql("SELECT t_user.name              AS \"name\",\n" +
                    "#        t_user.gender            AS \"gender\",\n" +
                    "       sd.label AS \"gender\",\n" +
                    "       t_user.age               AS \"age\",\n" +
                    "       t_user.birthday          AS \"birthday\",\n" +
                    "       t_user.mobile            AS \"mobile\",\n" +
                    "       t_user.email             AS \"email\",\n" +
                    "       t_user.native_place      AS \"nativePlace\",\n" +
                    "       t_user.hobby             AS \"hobbyList\",\n" +
                    "       IF(t_user.marriage, '是', '否')         AS \"marriage\",\n" +
                    "       t_user.introduce         AS \"introduce\",\n" +
                    "       t_user.attachment        AS \"attachmentList\",\n" +
                    "       t_user.school_experience AS \"schoolExperienceList\",\n" +
                    "       t_user.status            AS \"status\",\n" +
                    "       t_user.created_by        AS \"createdBy\",\n" +
                    "       t_user.created_at        AS \"createdAt\",\n" +
                    "       t_user.updated_by        AS \"updatedBy\",\n" +
                    "       t_user.updated_at        AS \"updatedAt\",\n" +
                    "       t_user.is_deleted        AS \"deleted\",\n" +
                    "       t_user.id                AS \"id\"\n" +
                    "FROM t_user left JOIN sys_dict sd ON t_user.gender = sd.name" +
                    " WHERE is_deleted = 0 AND t_user.name like :name AND t_user.gender = :gender AND t_user.mobile = :mobile " +
                    "AND t_user.native_place IN (:nativePlace) AND t_user.status = :status AND t_user.hobby like :hobbyList" +
                            " AND t_user.birthday >= :birthday0 AND t_user.birthday <= :birthday1 AND marriage = :marriage")
            .reportColumnList(Arrays.asList(
                    new ReportColumn("name", "姓名", true),
                    new ReportColumn("gender", "性别"),
                    new ReportColumn("age", "年龄"),
                    new ReportColumn("birthday", "出生日期",false, null, Arrays.asList("localDateConverter")),
                    new ReportColumn("mobile", "手机号码"),
                    new ReportColumn("email", "邮箱"),
                    new ReportColumn("nativePlace", "籍贯"),
                    new ReportColumn("hobbyList", "兴趣爱好", false, "hobbyList", Arrays.asList("arrayDictConverter")),
                    new ReportColumn("marriage", "婚否"),
                    new ReportColumn("introduce", "自我介绍"),
//                        new ReportColumn("attachmentList", "附件"),
//                        new ReportColumn("schoolExperienceList", "学习经历"),
                    new ReportColumn("status", "状态", false, "status", Arrays.asList("dictConverter"))
            ))
            .queryFieldList(Arrays.asList(
                    new QueryField("name", "姓名"),
                    new QueryField("mobile", "手机号码"),
                    // sys_dic#gender
                    new QueryField("gender", "性别", QueryField.Type.SELECT, "gender"),
                    new QueryField("birthday", "出生日期", QueryField.Type.DATE_RANGE),
                    // 配置文件
                    new QueryField("nativePlace", "籍贯", QueryField.Type.MULTIPLE_SELECT, "nativePlace"),
                    // 接口实现
                    new QueryField("hobbyList", "兴趣爱好", QueryField.Type.SELECT, "hobbyList"),
                    // 配置文件
                    new QueryField("status", "状态", QueryField.Type.SELECT, "status"),
                    new QueryField("marriage", "婚否", QueryField.Type.SELECT, "bol")
            ))
            .pageable(true)
//                .summary(true)
            .summaryColumnNames("age, mobile")
            .sidx("name")
            .sord(SordEnum.ASC)
            .build());
}

http://xhope.top/wp-content/uploads/2023/03/list.png
参数

SELECT t_user.name                   AS "name",
       sd.label                      AS "gender",
       t_user.age                    AS "age",
       t_user.birthday               AS "birthday",
       t_user.mobile                 AS "mobile",
       t_user.email                  AS "email",
       t_user.native_place           AS "nativePlace",
       t_user.hobby                  AS "hobbyList",
       IF(t_user.marriage, '是', '否') AS "marriage",
       t_user.introduce              AS "introduce",
       t_user.attachment             AS "attachmentList",
       t_user.school_experience      AS "schoolExperienceList",
       t_user.status                 AS "status",
       t_user.created_by             AS "createdBy",
       t_user.created_at             AS "createdAt",
       t_user.updated_by             AS "updatedBy",
       t_user.updated_at             AS "updatedAt",
       t_user.is_deleted             AS "deleted",
       t_user.id                     AS "id"
FROM t_user
         LEFT JOIN sys_dict sd ON t_user.gender = sd.name
WHERE is_deleted = 0
  AND UPPER(t_user.name) LIKE CONCAT('%', UPPER(:name), '%') ESCAPE
      '\\' AND t_user.gender = :gender AND t_user.mobile = :mobile AND t_user.native_place IN (:nativePlace0) AND
      t_user.status = :status AND t_user.birthday >= :birthday0 AND t_user.birthday <= :birthday1 AND
      marriage = :marriage

[{gender=2, marriage=1, name=凡夫俗子, mobile=17798765543, nativePlace0=上海, birthday1=1994-01-17, birthday0=1984-01-17, status=NORMAL}]