diff --git a/pom.xml b/pom.xml index 688c015..dbbf159 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ jar 材料管理系统 - 2.3.6.8-SNAPSHOT + 2.3.6.10-SNAPSHOT @@ -128,8 +128,28 @@ ${mjkf-xinke.version} + + + com.baomidou + mybatis-plus-generator + ${mybatis.plus.version} + + + + + org.freemarker + freemarker + + + + org.mitre.dsmiley.httpproxy + smiley-http-proxy-servlet + 1.12.1 + + + diff --git a/src/main/java/mjkf/xinke/core/config/GlobalConfigure.java b/src/main/java/mjkf/xinke/core/config/GlobalConfigure.java index 1311cc5..e1f2a55 100644 --- a/src/main/java/mjkf/xinke/core/config/GlobalConfigure.java +++ b/src/main/java/mjkf/xinke/core/config/GlobalConfigure.java @@ -74,7 +74,7 @@ import java.util.*; * @date 2021/10/9 14:24 **/ @Configuration -@MapperScan(basePackages = {"mjkf.xinke.**.mapper, com.bstek.**.mapper"}) +@MapperScan(basePackages = {"mjkf.xinke.**.mapper, mjkf.xinke.**.dao, com.bstek.**.mapper"}) public class GlobalConfigure implements WebMvcConfigurer { private static final String COMMON_REPEAT_SUBMIT_CACHE_KEY = "common-repeatSubmit:"; @@ -142,6 +142,8 @@ public class GlobalConfigure implements WebMvcConfigurer { "/pay/wx/authNotifyUrl", "/pay/wx/jsPay", "/pay/order/sample/doCreateOrder", + + "/solr/**", }; /** diff --git a/src/main/java/mjkf/xinke/core/handler/GlobalExceptionUtil.java b/src/main/java/mjkf/xinke/core/handler/GlobalExceptionUtil.java index 00ca200..4ce9131 100644 --- a/src/main/java/mjkf/xinke/core/handler/GlobalExceptionUtil.java +++ b/src/main/java/mjkf/xinke/core/handler/GlobalExceptionUtil.java @@ -5,6 +5,7 @@ import cn.dev33.satoken.exception.SaTokenException; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpStatus; +import com.jgy.xxs.core.http.exp.NcHttpException; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.exceptions.PersistenceException; import org.mybatis.spring.MyBatisSystemException; @@ -120,6 +121,10 @@ public class GlobalExceptionUtil { log.error(">>> 数据操作异常:", e); commonResult = CommonResult.error("数据操作异常"); } + } else if (e instanceof NcHttpException) { + // 通用业务异常,直接返回给前端 + NcHttpException ncHttpException = (NcHttpException) e; + commonResult = CommonResult.get(ncHttpException.getMetaCode(), ncHttpException.getMessage(), null); } else if (e instanceof CommonException) { // 通用业务异常,直接返回给前端 diff --git a/src/main/java/mjkf/xinke/main/common/generator/DataDbMpGenerator.java b/src/main/java/mjkf/xinke/main/common/generator/DataDbMpGenerator.java index f02442e..b9b127a 100644 --- a/src/main/java/mjkf/xinke/main/common/generator/DataDbMpGenerator.java +++ b/src/main/java/mjkf/xinke/main/common/generator/DataDbMpGenerator.java @@ -28,6 +28,10 @@ public class DataDbMpGenerator { "STEEL_PLATE", "STEEL_REBAR", "STEEL_STRAND", + "MATERIAL", + "MATERIAL_TASK", + "PRICE_PUBLISH", + "PRICE_RESULT", }; @@ -92,6 +96,7 @@ public class DataDbMpGenerator { .controllerBuilder() .enableRestStyle() + .enableHyphenStyle() ; } diff --git a/src/main/java/mjkf/xinke/main/common/http/FuHttpResponse.java b/src/main/java/mjkf/xinke/main/common/http/FuHttpResponse.java new file mode 100644 index 0000000..9c06dca --- /dev/null +++ b/src/main/java/mjkf/xinke/main/common/http/FuHttpResponse.java @@ -0,0 +1,36 @@ +package mjkf.xinke.main.common.http; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.jgy.xxs.core.http.NcMeta; +import com.jgy.xxs.core.http.NcPagination; +import com.jgy.xxs.core.http.resp.HttpResponse; +import com.jgy.xxs.core.http.resp.NcHttpResponse; +import lombok.Getter; + + +@Getter +public class FuHttpResponse extends NcHttpResponse { + @JsonIgnore + private NcMeta meta; + @JsonIgnore + private NcPagination pagination; + + private String msg; + private int code; + private T data; + + public static HttpResponse Builder() { + return new FuHttpResponse(); + } + + @Override + public HttpResponse build() { + this.msg = this._message; + this.code = this._code; + this.data = this._data; + return this; + } +} + + + diff --git a/src/main/java/mjkf/xinke/main/constant/HttpErrorResponseEnum.java b/src/main/java/mjkf/xinke/main/constant/HttpErrorResponseEnum.java new file mode 100644 index 0000000..74d030e --- /dev/null +++ b/src/main/java/mjkf/xinke/main/constant/HttpErrorResponseEnum.java @@ -0,0 +1,51 @@ +package mjkf.xinke.main.constant; + +import com.jgy.xxs.core.http.exp.NcHttpErrorResponseInterface; +import org.apache.http.HttpStatus; + +/** + * HttpErrorResponseEnum + * + * @author han0 + * @date 2019-08-19 + */ +public enum HttpErrorResponseEnum implements NcHttpErrorResponseInterface { + // 通用异常 + SYSTEM_ERROR("系统异常", 500, HttpStatus.SC_INTERNAL_SERVER_ERROR), + BAD_REQUEST("请求数据错误!", 400, HttpStatus.SC_BAD_REQUEST), + FORBIDDEN("没权限访问,请退出重新登录!", 403, HttpStatus.SC_FORBIDDEN), + NOT_FOUND("找不到指定资源", 404, HttpStatus.SC_NOT_FOUND), + // + MATERIAL_NOT_FOUND("找不到指定材料", 404, HttpStatus.SC_NOT_FOUND), + MATERIAL_CANNOT_DELETE_BUILTIN("内建材料不允许删除", 400, HttpStatus.SC_NOT_FOUND), + MATERIAL_ID_REPEAT("材料 id 重复", 400, HttpStatus.SC_NOT_FOUND), + MATERIAL_ID_INVALID("材料 id 无效", 400, HttpStatus.SC_NOT_FOUND), + MATERIAL_PARENT_ID_INVALID("材料 parent_id 无效", 400, HttpStatus.SC_NOT_FOUND), + ; + + private String message; + private int code; + private int httpStatus; + + HttpErrorResponseEnum(String message, int code, int httpStatus) { + this.message = message; + this.code = code; + this.httpStatus = httpStatus; + } + + @Override + public String getMessage() { + return this.message; + } + + @Override + public int getCode() { + return this.code; + } + + @Override + public int getHttpStatusCode() { + return this.httpStatus; + } + +} diff --git a/src/main/java/mjkf/xinke/main/controller/MaterialController.java b/src/main/java/mjkf/xinke/main/controller/MaterialController.java new file mode 100644 index 0000000..3c2d94d --- /dev/null +++ b/src/main/java/mjkf/xinke/main/controller/MaterialController.java @@ -0,0 +1,136 @@ +package mjkf.xinke.main.controller; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.jgy.xxs.core.common.util.CommonUtil; +import com.jgy.xxs.core.http.exp.NcHttpException; +import com.jgy.xxs.core.http.resp.HttpResponse; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import mjkf.xinke.auth.core.pojo.SaBaseLoginUser; +import mjkf.xinke.auth.core.util.StpLoginUserUtil; +import mjkf.xinke.main.common.http.FuHttpResponse; +import mjkf.xinke.main.constant.HttpErrorResponseEnum; +import mjkf.xinke.main.model.db.Material; +import mjkf.xinke.main.model.vo.MaterialCreateRequest; +import mjkf.xinke.main.model.vo.MaterialEditRequest; +import mjkf.xinke.main.service.MaterialService; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + *

+ * 材料 前端控制器 + *

+ * + * @author han0 + * @since 2023-11-09 + */ +@RestController +@RequestMapping("/material") +public class MaterialController { + @Resource + MaterialService materialService; + + @ApiOperation("获取材料树") + @GetMapping("/tree") + public HttpResponse getTree () { + QueryWrapper query = new QueryWrapper<>(); + List> maps = materialService.listMaps(query); + maps = maps.stream().map(map -> convertKeysToLowercase(map)).collect(Collectors.toList()); + // todo 列转树改为泛型对象入参 + List> result = CommonUtil.listToTree(maps, Material.Fields.id, StrUtil.toUnderlineCase(Material.Fields.parentId), "00.00.00.00"); + return FuHttpResponse.Builder().dataResponse(result).build(); + } + + private Map convertKeysToLowercase(Map originalMap) { + Map resultMap = new HashMap<>(); + // 遍历原始Map的entry,将key转为小写 + for (Map.Entry entry : originalMap.entrySet()) { + String lowercaseKey = entry.getKey().toLowerCase(); + resultMap.put(lowercaseKey, entry.getValue()); + } + return resultMap; + } + + @ApiOperation("获取材料列表") + @GetMapping("/") + public HttpResponse list ( + @ApiParam(value = "关键字") String keyWord, + @ApiParam(value = "父节点id") String parentId + ) { + LambdaQueryWrapper query = new LambdaQueryWrapper<>(); + if (parentId != null){ + query.eq(Material::getParentId, parentId); + } + query.and(q -> q + .like(Material::getCategory1, keyWord).or() + .like(Material::getCategory2, keyWord).or() + .like(Material::getCategory3, keyWord).or() + .like(Material::getCategory4, keyWord) + ); + var result = materialService.list(query); + + return FuHttpResponse.Builder().dataResponse(result).build(); + } + + @ApiOperation("删除材料") + @DeleteMapping("/{id}") + public HttpResponse delete ( + @PathVariable String id + ) throws Exception { + var data = materialService.getById(id); + if (data == null) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_NOT_FOUND); + } + if (data.isBuiltin()) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_CANNOT_DELETE_BUILTIN); + } + materialService.removeById(id); + + return FuHttpResponse.Builder().dataResponse().build(); + } + + @ApiOperation("新增材料") + @PostMapping("/") + public HttpResponse create ( + @ApiParam("参数") @RequestBody MaterialCreateRequest params + ) throws Exception { + params.check(); + var user = StpLoginUserUtil.getLoginUser(); + + var data = materialService.getById(params.getId()); + if (data != null) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_ID_REPEAT); + } + + data = new Material(params, user); + materialService.save(data); + + return FuHttpResponse.Builder().dataResponse(data).build(); + } + + @ApiOperation("编辑材料") + @PutMapping("/{id}") + public HttpResponse edit ( + @PathVariable String id, + @ApiParam("参数") @RequestBody MaterialEditRequest params + ) throws Exception { + params.check(); + var user = StpLoginUserUtil.getLoginUser(); + + var data = materialService.getById(id); + if (data == null) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_NOT_FOUND); + } + data.edit(params, user); + + return FuHttpResponse.Builder().dataResponse(data).build(); + } +} diff --git a/src/main/java/mjkf/xinke/main/dao/MaterialMapper.java b/src/main/java/mjkf/xinke/main/dao/MaterialMapper.java new file mode 100644 index 0000000..ae0390f --- /dev/null +++ b/src/main/java/mjkf/xinke/main/dao/MaterialMapper.java @@ -0,0 +1,16 @@ +package mjkf.xinke.main.dao; + +import mjkf.xinke.main.model.db.Material; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + *

+ * 材料 Mapper 接口 + *

+ * + * @author han0 + * @since 2023-11-10 + */ +public interface MaterialMapper extends BaseMapper { + +} diff --git a/src/main/java/mjkf/xinke/main/model/db/Material.java b/src/main/java/mjkf/xinke/main/model/db/Material.java new file mode 100644 index 0000000..acd4717 --- /dev/null +++ b/src/main/java/mjkf/xinke/main/model/db/Material.java @@ -0,0 +1,148 @@ +package mjkf.xinke.main.model.db; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import java.io.Serializable; +import java.time.LocalDateTime; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldNameConstants; +import mjkf.xinke.auth.core.pojo.SaBaseLoginUser; +import mjkf.xinke.main.model.vo.MaterialCreateRequest; +import mjkf.xinke.main.model.vo.MaterialEditRequest; + +/** + *

+ * 材料 + *

+ * + * @author han0 + * @since 2023-11-10 + */ +@Getter +@Setter +@FieldNameConstants +@TableName("MATERIAL") +@ApiModel(value = "Material对象", description = "材料") +public class Material extends Model { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("最后更新人id") + @TableField("UPDATE_USER_ID") + private String updateUserId; + + @ApiModelProperty("最后更新人名称") + @TableField("UPDATE_USER_NAME") + private String updateUserName; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty("最后更新时间") + @TableField("UPDATE_TIME") + private LocalDateTime updateTime; + + @ApiModelProperty("创建人id") + @TableField("CREATE_USER_ID") + private String createUserId; + + @ApiModelProperty("创建人名称") + @TableField("CREATE_USER_NAME") + private String createUserName; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty("创建时间") + @TableField("CREATE_TIME") + private LocalDateTime createTime; + + @ApiModelProperty("删除人id") + @TableField("DELETE_USER_ID") + private String deleteUserId; + + @ApiModelProperty("删除人名称") + @TableField("DELETE_USER_NAME") + private String deleteUserName; + + @JsonSerialize(using = LocalDateTimeSerializer.class) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @ApiModelProperty("删除时间") + @TableField("DELETE_TIME") + private LocalDateTime deleteTime; + + @TableId(value = "ID", type = IdType.AUTO) + private String id; + + @TableField("PARENT_ID") + private String parentId; + + @ApiModelProperty("分类1") + @TableField("CATEGORY1") + private String category1; + + @ApiModelProperty("分类2") + @TableField("CATEGORY2") + private String category2; + + @ApiModelProperty("分类3") + @TableField("CATEGORY3") + private String category3; + + @ApiModelProperty("分类4") + @TableField("CATEGORY4") + private String category4; + + @ApiModelProperty("是否初始内建类型(不允许删除)") + @TableField("IS_BUILTIN") + private Integer isBuiltin; + + @ApiModelProperty("材料类别(主材、地材)") + @TableField("`TYPE`") + private Integer type; + + @Override + public Serializable pkVal() { + return this.id; + } + + public boolean isBuiltin() { + if (this.isBuiltin == null) { + return false; + } + return this.isBuiltin.equals(1); + } + + public Material() {} + + public Material(MaterialCreateRequest params, SaBaseLoginUser user) { + this.id = params.getId(); + this.parentId = params.getParentId(); + this.category1 = params.getCategory1(); + this.category2 = params.getCategory2(); + this.category3 = params.getCategory3(); + this.category4 = params.getCategory4(); + this.updateTime = LocalDateTime.now(); + this.createTime = LocalDateTime.now(); + this.createUserName = user.getName(); + this.createUserId = user.getId(); + } + + public void edit(MaterialEditRequest params, SaBaseLoginUser user) { + this.category1 = params.getCategory1(); + this.category2 = params.getCategory2(); + this.category3 = params.getCategory3(); + this.category4 = params.getCategory4(); + this.updateTime = LocalDateTime.now(); + this.updateUserName = user.getName(); + this.updateUserId = user.getId(); + } +} diff --git a/src/main/java/mjkf/xinke/main/model/vo/MaterialCreateRequest.java b/src/main/java/mjkf/xinke/main/model/vo/MaterialCreateRequest.java new file mode 100644 index 0000000..4bb88c7 --- /dev/null +++ b/src/main/java/mjkf/xinke/main/model/vo/MaterialCreateRequest.java @@ -0,0 +1,47 @@ +package mjkf.xinke.main.model.vo; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.jgy.xxs.core.http.exp.NcHttpException; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import mjkf.xinke.main.constant.HttpErrorResponseEnum; + +@Data +public class MaterialCreateRequest { + + @TableId(value = "ID", type = IdType.AUTO) + private String id; + + @ApiModelProperty("父级id") + private String parentId; + + @ApiModelProperty("分类1") + private String category1; + + @ApiModelProperty("分类2") + private String category2; + + @ApiModelProperty("分类3") + private String category3; + + @ApiModelProperty("分类4") + private String category4; + + public void check() throws Exception{ + String flag = this.parentId.replace(".00", ""); + if (!this.id.contains(flag)) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_ID_INVALID); + } + if ((int) this.id.chars().filter(c -> c == '.').count() != 3) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_ID_INVALID); + } + if ((int) this.parentId.chars().filter(c -> c == '.').count() != 3) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_PARENT_ID_INVALID); + } + if (this.parentId.chars().filter(c -> c == '0').count() < this.id.chars().filter(c -> c == '0').count()) { + throw new NcHttpException(HttpErrorResponseEnum.MATERIAL_ID_INVALID); + } + } + +} diff --git a/src/main/java/mjkf/xinke/main/model/vo/MaterialEditRequest.java b/src/main/java/mjkf/xinke/main/model/vo/MaterialEditRequest.java new file mode 100644 index 0000000..35f4ac7 --- /dev/null +++ b/src/main/java/mjkf/xinke/main/model/vo/MaterialEditRequest.java @@ -0,0 +1,25 @@ +package mjkf.xinke.main.model.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class MaterialEditRequest { + + @ApiModelProperty("分类1") + private String category1; + + @ApiModelProperty("分类2") + private String category2; + + @ApiModelProperty("分类3") + private String category3; + + @ApiModelProperty("分类4") + private String category4; + + + public void check() throws Exception{ + + } +} diff --git a/src/main/java/mjkf/xinke/main/service/MaterialService.java b/src/main/java/mjkf/xinke/main/service/MaterialService.java new file mode 100644 index 0000000..f2b3b58 --- /dev/null +++ b/src/main/java/mjkf/xinke/main/service/MaterialService.java @@ -0,0 +1,12 @@ +package mjkf.xinke.main.service; + +import mjkf.xinke.main.model.db.Material; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +@Service +public class MaterialService extends ServiceImpl, Material> { + +} + diff --git a/src/main/resources/template/service.java.ftl b/src/main/resources/template/service.java.ftl new file mode 100644 index 0000000..c6da43b --- /dev/null +++ b/src/main/resources/template/service.java.ftl @@ -0,0 +1,16 @@ +package ${package.Service}; + +import ${package.Entity}.${entity}; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import ${superServiceClassPackage}; +import org.springframework.stereotype.Service; + + <#if kotlin> +class ${entity}Service : ${superServiceClass}, ${entity}> + <#else> +@Service +public class ${entity}Service extends ${superServiceClass}, ${entity}> { + +} + +