commit bfe1ce85354cff5beb3ffc4d9f4aaf31a3224756 Author: caoyiwen Date: Sun Oct 8 02:31:55 2023 +0800 init diff --git a/diet-core/pom.xml b/diet-core/pom.xml new file mode 100644 index 0000000..de56be8 --- /dev/null +++ b/diet-core/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.mathvision.diet + diet-core + 1.0-SNAPSHOT + + + 8 + 8 + UTF-8 + + + + + com.mathvision.diet + diet-dao + 1.0-SNAPSHOT + + + com.alibaba + easyexcel + 3.3.2 + + + \ No newline at end of file diff --git a/diet-core/src/main/java/com/mathvision/diet/domain/ComponentAnalysisDO.java b/diet-core/src/main/java/com/mathvision/diet/domain/ComponentAnalysisDO.java new file mode 100644 index 0000000..a857965 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/domain/ComponentAnalysisDO.java @@ -0,0 +1,17 @@ +package com.mathvision.diet.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ComponentAnalysisDO { + + String name; + String nutrition; + String nvr; +} diff --git a/diet-core/src/main/java/com/mathvision/diet/domain/DishLabelDO.java b/diet-core/src/main/java/com/mathvision/diet/domain/DishLabelDO.java new file mode 100644 index 0000000..4cac6a7 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/domain/DishLabelDO.java @@ -0,0 +1,18 @@ +package com.mathvision.diet.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DishLabelDO { + String name; + List component; + List ingredients; +} diff --git a/diet-core/src/main/java/com/mathvision/diet/domain/UserDO.java b/diet-core/src/main/java/com/mathvision/diet/domain/UserDO.java new file mode 100644 index 0000000..9881c3c --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/domain/UserDO.java @@ -0,0 +1,33 @@ +package com.mathvision.diet.domain; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.entity.RoleItem; +import com.mathvision.diet.entity.Vender; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.util.List; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserDO { + private String uid; + private String name; + private String phone; + private Vender vender; + private Long roleId; + private String roleName; + private String roleType; + private Instant time; + private List roleItems; + + @JSONField(serialize = false) + public boolean isAdmin() { + return vender == null; + } +} \ No newline at end of file diff --git a/diet-core/src/main/java/com/mathvision/diet/excel/BigDecimalStringConverter.java b/diet-core/src/main/java/com/mathvision/diet/excel/BigDecimalStringConverter.java new file mode 100644 index 0000000..7a2473f --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/excel/BigDecimalStringConverter.java @@ -0,0 +1,43 @@ +package com.mathvision.diet.excel; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.GlobalConfiguration; +import com.alibaba.excel.metadata.data.ReadCellData; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.metadata.property.ExcelContentProperty; +import com.alibaba.excel.util.NumberUtils; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.text.ParseException; + +public class BigDecimalStringConverter implements Converter { + public BigDecimalStringConverter() { + } + + public Class supportJavaTypeKey() { + return BigDecimal.class; + } + + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.STRING; + } + + public BigDecimal convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws ParseException { + if(cellData == null) { + return null; + } + if(CellDataTypeEnum.NUMBER.equals(cellData.getType())) { + return cellData.getNumberValue(); + } + if (CellDataTypeEnum.STRING.equals(cellData.getType())) { + return NumberUtils.parseBigDecimal(cellData.getStringValue(), contentProperty); + } + return null; + } + + public WriteCellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) { + return NumberUtils.formatToCellDataString(value, contentProperty); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/excel/IngredientModel.java b/diet-core/src/main/java/com/mathvision/diet/excel/IngredientModel.java new file mode 100644 index 0000000..750dd31 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/excel/IngredientModel.java @@ -0,0 +1,69 @@ +package com.mathvision.diet.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@HeadRowHeight(30) +@ContentRowHeight(15) +@ColumnWidth(25) +@ContentFontStyle(fontHeightInPoints = (short) 12) +public class IngredientModel { + + @ExcelProperty("食物编码") + private String key; + + @ExcelProperty("食物名称") + private String name; + + @ExcelProperty("食物类型") + private String type; + + @ExcelProperty(value = "能量(kcal)", converter = BigDecimalStringConverter.class) + private BigDecimal energy; + + @ExcelProperty(value = "蛋白质(g)", converter = BigDecimalStringConverter.class) + private BigDecimal protein; + + @ExcelProperty(value = "脂肪(g)", converter = BigDecimalStringConverter.class) + private BigDecimal fat; + + @ExcelProperty(value = "碳水化合物(g)", converter = BigDecimalStringConverter.class) + private BigDecimal carbs; + + @ExcelProperty(value = "钙(mg)", converter = BigDecimalStringConverter.class) + private BigDecimal calcium; + + @ExcelProperty(value = "铁(mg)", converter = BigDecimalStringConverter.class) + private BigDecimal iron; + + @ExcelProperty(value = "锌(mg)", converter = BigDecimalStringConverter.class) + private BigDecimal zinc; + + @ExcelProperty(value = "维生素A(μgRAE)", converter = BigDecimalStringConverter.class) + private BigDecimal va; + + @ExcelProperty(value = "维生素B1(mg)硫胺素", converter = BigDecimalStringConverter.class) + private BigDecimal vb1; + + @ExcelProperty(value = "维生素B2(mg)核黄素", converter = BigDecimalStringConverter.class) + private BigDecimal vb2; + + @ExcelProperty(value = "维生素C(mg)", converter = BigDecimalStringConverter.class) + private BigDecimal vc; + + @ExcelProperty(value = "膳食纤维(g)", converter = BigDecimalStringConverter.class) + private BigDecimal fiber; +} diff --git a/diet-core/src/main/java/com/mathvision/diet/excel/ResultModel.java b/diet-core/src/main/java/com/mathvision/diet/excel/ResultModel.java new file mode 100644 index 0000000..0f16ae3 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/excel/ResultModel.java @@ -0,0 +1,31 @@ +package com.mathvision.diet.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentFontStyle; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@NoArgsConstructor +@AllArgsConstructor +@HeadRowHeight(30) +@ContentRowHeight(15) +@ColumnWidth(18) +@ContentFontStyle(fontHeightInPoints = (short) 12) +public class ResultModel { + + @ColumnWidth(80) + @ExcelProperty("数据标识") + private String key; + + @ColumnWidth(35) + @ExcelProperty("导入结果") + private String result; + +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/DishService.java b/diet-core/src/main/java/com/mathvision/diet/service/DishService.java new file mode 100644 index 0000000..c656c48 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/DishService.java @@ -0,0 +1,147 @@ +package com.mathvision.diet.service; + +import com.google.common.collect.Lists; +import com.mathvision.diet.domain.ComponentAnalysisDO; +import com.mathvision.diet.domain.DishLabelDO; +import com.mathvision.diet.domian.DishItemDTO; +import com.mathvision.diet.entity.Dish; +import com.mathvision.diet.entity.FoodNutrient; +import com.mathvision.diet.entity.Ingredient; +import com.mathvision.diet.repository.DishRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import javax.persistence.criteria.Predicate; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class DishService { + + @Resource + private EnumService enumService; + + @Resource + private IngredientService ingredientService; + + @Resource + private DishRepository dishRepository; + + public void add(List dishes, String operator) { + dishRepository.saveAll(dishes); + log.info("[DishService] add dishes count = " + dishes.size() + ", operator = " + operator); + } + + public void delete(List ids, Long venderId,String operator) { + if (venderId > 0) { + dishRepository.deleteByIdInAndVender(ids, venderId); + } else { + dishRepository.deleteAllByIdInBatch(ids); + } + log.info("[DishService] delete ids = " + ids + ", operator = " + operator); + } + + public void delete(Long venderId,String operator) { + dishRepository.deleteByVender(venderId); + log.info("[DishService] delete venderId = " + venderId + ", operator = " + operator); + } + + public void update(Dish dish, String operator) { + dishRepository.save(dish); + log.info("[DishService] update name = " + dish.getName() + ", operator = " + operator); + } + + public boolean exists(Long vender, List ids) { + return dishRepository.existsByVenderAndIdIn(vender, ids); + } + + public boolean exists(Long id, String name, Long vender) { + return id == null ? dishRepository.existsByVenderAndName(vender, name) : dishRepository.existsByVenderAndNameAndIdNot(vender, name, id); + } + + public Dish get(Long id) { + return dishRepository.findById(id).orElse(null); + } + + public Dish get(Long id, Long vender) { + return dishRepository.findByIdAndVender(id, vender); + } + + public List query(String keyword) { + return dishRepository.findByNameLikeOrderByIdDesc("%" + keyword + "%"); + } + + public Page list(Long vender, String name, String mark, PageRequest pageRequest) { + return dishRepository.findAll(toSpecification(vender, name, mark), pageRequest); + } + + private Specification toSpecification(Long vender, String name, String mark) { + return (root, query, builder) -> { + List predicates = new ArrayList<>(); + + if (vender > 0) { + predicates.add(builder.equal(root.get("vender"), vender)); + } + + if (StringUtils.isNotBlank(mark)) { + predicates.add(builder.equal(root.get("marks"), mark)); + } + + if (StringUtils.isNotBlank(name)) { + predicates.add(builder.like(root.get("name"), name + "%")); + } + + if (predicates.size() > 1) { + return builder.and(predicates.toArray(new Predicate[0])); + } else if (predicates.size() == 1) { + return predicates.get(0); + } else { + return null; + } + }; + } + + public List label(List ids, Long vender) { + List dishes = Lists.newArrayList(); + if (CollectionUtils.isNotEmpty(ids)) { + if (vender > 0) { + dishes.addAll(dishRepository.findByVenderAndIdIn(vender, ids)); + } else { + dishes.addAll(dishRepository.findByIdIn(ids)); + } + } else { + if (vender > 0) { + dishes.addAll(dishRepository.findByVender(vender)); + } else { + dishes.addAll(dishRepository.findAll()); + } + } + + Map ingredientMap = ingredientService.getFullByKeys(dishes.stream().filter(x -> CollectionUtils.isNotEmpty(x.getIngredient())).flatMap(x -> x.getIngredient().stream().map(DishItemDTO::getKey)).collect(Collectors.toSet())).stream().collect(Collectors.toMap(Ingredient::getKey, v -> v)); + return dishes.parallelStream().filter(dish -> CollectionUtils.isNotEmpty(dish.getIngredient())).map(dish -> { + List ingredients = dish.getIngredient().stream().filter(DishItemDTO::getIsMain).map(DishItemDTO::getKey).filter(ingredientMap::containsKey).map(x -> ingredientMap.get(x).getName()).collect(Collectors.toList()); + List component = dish.getIngredient().stream().filter(x -> ingredientMap.containsKey(x.getKey())).flatMap(x -> { + Ingredient ingredient = ingredientMap.get(x.getKey()); + return ingredient.getNutrient().entrySet().stream().map(n -> Pair.of(n.getKey(), n.getValue().multiply(x.getValue()).divide(new BigDecimal(100), RoundingMode.HALF_UP))); + }).collect(Collectors.toMap(Pair::getKey, Pair::getValue, BigDecimal::add)).entrySet().stream().map(r -> { + FoodNutrient foodNutrient = enumService.getNutrient(r.getKey()); + return foodNutrient == null ? + ComponentAnalysisDO.builder().name(r.getKey()).nutrition(String.format("%.2f(-)", r.getValue())).nvr("-").build() : + ComponentAnalysisDO.builder().name(foodNutrient.getValue()).nutrition(String.format("%.2f(%s)", r.getValue().floatValue(), foodNutrient.getMeasurement())).nvr(foodNutrient.getNrv() == null || foodNutrient.getNrv().floatValue() ==0 ? "-" : String.format("%.2f%%", r.getValue().divide(foodNutrient.getNrv(), RoundingMode.HALF_UP))).build(); + }).collect(Collectors.toList()); + return DishLabelDO.builder().name(dish.getName()).ingredients(ingredients).component(component).build(); + }).collect(Collectors.toList()); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/EnumService.java b/diet-core/src/main/java/com/mathvision/diet/service/EnumService.java new file mode 100644 index 0000000..220045c --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/EnumService.java @@ -0,0 +1,135 @@ +package com.mathvision.diet.service; + +import com.google.common.collect.Sets; +import com.mathvision.diet.domian.*; +import com.mathvision.diet.entity.FoodCategory; +import com.mathvision.diet.entity.FoodMark; +import com.mathvision.diet.entity.FoodNutrient; +import com.mathvision.diet.entity.RoleItem; +import com.mathvision.diet.repository.FoodCategoryRepository; +import com.mathvision.diet.repository.FoodMarkRepository; +import com.mathvision.diet.repository.FoodNutrientRepository; +import com.mathvision.diet.repository.RoleItemRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.KeyValue; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class EnumService { + private static Map> AUTH_ITEMS = new HashMap<>(); + private static Map ROLE_ITEMS = new HashMap<>(); + private static Map FOOD_MARK = new HashMap<>(); + private static Map FOOD_CATEGORY = new HashMap<>(); + private static Map FOOD_NUTRIENT = new HashMap<>(); + + @Resource + private RoleItemRepository roleItemRepository; + + @Resource + private FoodMarkRepository foodMarkRepository; + + @Resource + private FoodCategoryRepository foodCategoryRepository; + + @Resource + private FoodNutrientRepository foodNutrientRepository; + + @PostConstruct + @Scheduled(cron = "0 0/5 * * * *") + public void init() { + ROLE_ITEMS = roleItemRepository.findAll().stream().collect(Collectors.toConcurrentMap(RoleItem::getId, x -> x)); + AUTH_ITEMS = ROLE_ITEMS.values().stream().map(item -> Pair.of(item.getItemType(), "/api/" + item.getItemValue().split(":", 2)[1])).collect(Collectors.toMap(Pair::getKey, v-> Sets.newHashSet(v.getValue()), Sets::union)); + FOOD_MARK = foodMarkRepository.findAll().stream().collect(Collectors.toConcurrentMap(FoodMark::getKey, x -> x)); + FOOD_CATEGORY = foodCategoryRepository.findAll().stream().collect(Collectors.toConcurrentMap(FoodCategory::getKey, x -> x)); + FOOD_NUTRIENT = foodNutrientRepository.findAll().stream().collect(Collectors.toConcurrentMap(FoodNutrient::getKey, x -> x)); + } + + public Map> getAll() { + Map> result = new HashMap<>(); + result.put("mark", FOOD_MARK.values()); + result.put("category", FOOD_CATEGORY.values()); + result.put("nutrient", FOOD_NUTRIENT.values()); + result.put("venderType", Arrays.stream(VenderType.values()).map(x -> new KeyValue() { + @Override + public String getKey() { + return x.getType(); + } + + @Override + public String getValue() { + return x.getType(); + } + }).collect(Collectors.toList())); + result.put("markType", Arrays.stream(MarkType.values()).map(x -> new KeyValue() { + @Override + public String getKey() { + return x.getType(); + } + + @Override + public String getValue() { + return x.getType(); + } + }).collect(Collectors.toList())); + result.put("mealType", Arrays.stream(MealType.values()).map(x -> new KeyValue() { + @Override + public String getKey() { + return x.getType(); + } + + @Override + public String getValue() { + return x.getType(); + } + }).collect(Collectors.toList())); + result.put("menuStatus", Arrays.stream(MenuStatus.values()).map(x -> new KeyValue() { + @Override + public Integer getKey() { + return x.getCode(); + } + + @Override + public String getValue() { + return x.getType(); + } + }).collect(Collectors.toList())); + + return result; + } + + public Set getAuthItems(AuthType authType) { + return AUTH_ITEMS.getOrDefault(authType, Sets.newHashSet()); + } + + public List getRoleItems(AuthType itemType) { + return ROLE_ITEMS.values().stream().filter(item -> item.getItemType().equals(itemType)).collect(Collectors.toList()); + } + + public List getRoleItems(List items) { + return ROLE_ITEMS.entrySet().stream().filter(x -> items.contains(x.getKey())).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public boolean checkMark(String mark) { + return FOOD_MARK.containsKey(mark); + } + + public boolean checkCategory(String category) { + return FOOD_CATEGORY.containsKey(category); + } + + public boolean checkNutrient(String nutrient) { + return FOOD_NUTRIENT.containsKey(nutrient); + } + + public FoodNutrient getNutrient(String nutrient) { + return FOOD_NUTRIENT.get(nutrient); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/IngredientService.java b/diet-core/src/main/java/com/mathvision/diet/service/IngredientService.java new file mode 100644 index 0000000..92deff3 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/IngredientService.java @@ -0,0 +1,223 @@ +package com.mathvision.diet.service; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.read.listener.ReadListener; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.google.common.collect.Lists; +import com.mathvision.diet.domian.MarkType; +import com.mathvision.diet.entity.Ingredient; +import com.mathvision.diet.entity.IngredientMark; +import com.mathvision.diet.excel.IngredientModel; +import com.mathvision.diet.excel.ResultModel; +import com.mathvision.diet.repository.IngredientDTORepository; +import com.mathvision.diet.repository.IngredientMarkRepository; +import com.mathvision.diet.repository.IngredientRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class IngredientService { + + @Resource + private EnumService enumService; + + @Resource + private IngredientRepository ingredientRepository; + + @Resource + private IngredientDTORepository ingredientDTORepository; + + @Resource + private IngredientMarkRepository ingredientMarkRepository; + + public void template(OutputStream outputStream) { + EasyExcel.write(outputStream).head(IngredientModel.class).excelType(ExcelTypeEnum.XLSX).sheet("模板").doWrite(Lists.newArrayList()); + } + + public void upload(InputStream inputStream, OutputStream outputStream, String operator) { + Instant dateTime = Instant.now(); + EasyExcel.read(inputStream, IngredientModel.class, new ReadListener() { + final List resultModels = Lists.newArrayList(); + final List ingredients = Lists.newArrayList(); + + @Override + public void onException(Exception exception, AnalysisContext context) { + Assert.isTrue(exception == null, "导入异常:" + exception.getMessage()); + } + + @Override + public void invoke(IngredientModel o, AnalysisContext analysisContext) { + String resultKey = String.format("%s-%s-%s", o.getKey(), o.getName(), o.getType()); + try { + Ingredient ingredient = Ingredient.builder().key(o.getKey()).name(o.getName()).type(o.getType()).nutrient(Arrays.stream(IngredientModel.class.getDeclaredFields()).filter(x -> BigDecimal.class.equals(x.getType())).peek(x -> x.setAccessible(true)).map(x -> { + try { + return Pair.of(x.getName(), (BigDecimal)x.get(o)); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + }).filter(x -> x.getValue() != null).collect(Collectors.toMap(Pair::getKey, Pair::getValue))).operate(operator).created(dateTime).modify(dateTime).build(); + + if (StringUtils.isBlank(ingredient.getKey())) { + resultModels.add(ResultModel.builder().key(resultKey).result("食物编码为空").build()); + return; + } + if (StringUtils.isBlank(ingredient.getName())) { + resultModels.add(ResultModel.builder().key(resultKey).result("食物名称为空").build()); + return; + } + if (ingredientRepository.existsByKeyOrName(ingredient.getKey(), ingredient.getName())) { + resultModels.add(ResultModel.builder().key(resultKey).result("编号/名称已存在").build()); + return; + } + if (ingredients.stream().anyMatch(x -> x.getKey().equals(ingredient.getKey()))) { + resultModels.add(ResultModel.builder().key(resultKey).result("编号在excel文件中存在重复").build()); + return; + } + if (StringUtils.isBlank(ingredient.getType()) || !enumService.checkCategory(ingredient.getType())) { + resultModels.add(ResultModel.builder().key(resultKey).result("食物类型为空或者不支持").build()); + return; + } + if (MapUtils.isEmpty(ingredient.getNutrient()) || !ingredient.getNutrient().entrySet().stream().allMatch(x -> enumService.checkNutrient(x.getKey()))) { + resultModels.add(ResultModel.builder().key(resultKey).result("营养素内容全空或者不支持").build()); + return; + } + + ingredients.add(ingredient); + resultModels.add(ResultModel.builder().key(resultKey).result("导入成功").build()); + } catch (Exception e) { + resultModels.add(ResultModel.builder().key(resultKey).result("导入失败:" + e.getMessage()).build()); + log.error(String.format("%s-%s-%s 导入失败: %s", o.getKey(), o.getName(), o.getType(), e.getMessage()), e); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + if (!ingredients.isEmpty()) { + ingredientRepository.saveAll(ingredients); + } + EasyExcel.write(outputStream).head(ResultModel.class).excelType(ExcelTypeEnum.XLSX).sheet("导入结果").doWrite(resultModels); + } + }).sheet().doRead(); + } + + public void addIngredient(Ingredient ingredient, String operator) { + Assert.isTrue(StringUtils.isNotBlank(ingredient.getKey()) && + StringUtils.isNotBlank(ingredient.getName()) && + StringUtils.isNotBlank(ingredient.getType()) && + MapUtils.isNotEmpty(ingredient.getNutrient()) && + enumService.checkCategory(ingredient.getType()) && + ingredient.getNutrient().entrySet().stream().allMatch(x -> enumService.checkNutrient(x.getKey())) && + !ingredientRepository.existsByKeyOrName(ingredient.getKey(), ingredient.getName()), + "[参数错误]必填参数未填写,或者编号名称重复、类型和营养素不在取值范围内!"); + + Instant dateTime = Instant.now(); + ingredient.setOperate(operator); + ingredient.setCreated(dateTime); + ingredient.setModify(dateTime); + ingredientRepository.save(ingredient); + log.info("[IngredientService] addIngredient name = " + ingredient.getName() + ", operator = " + operator); + } + + public void delIngredient(List ingredients, String operator) { + ingredients.stream().filter(StringUtils::isNotBlank).forEach(ingredient -> { + ingredientRepository.deleteByKey(ingredient); + ingredientMarkRepository.deleteByIngredient(ingredient); + log.info("[IngredientService] delIngredient ingredient = " + ingredient + ", operator = " + operator); + }); + } + + public void modIngredient(Ingredient ingredient, String operator) { + Assert.isTrue(StringUtils.isNotBlank(ingredient.getKey()) && + StringUtils.isNotBlank(ingredient.getName()) && + StringUtils.isNotBlank(ingredient.getType()) && + MapUtils.isNotEmpty(ingredient.getNutrient()) && + enumService.checkCategory(ingredient.getType()) && + ingredient.getNutrient().entrySet().stream().allMatch(x -> enumService.checkNutrient(x.getKey())) && + !ingredientRepository.existsByIdNotAndKeyOrName(ingredient.getId(), ingredient.getKey(), ingredient.getName()), + "[参数错误]必填参数未填写,或者编号名称重复、类型和营养素不在取值范围内!"); + Instant dateTime = Instant.now(); + ingredient.setOperate(operator); + ingredient.setCreated(dateTime); + ingredient.setModify(dateTime); + ingredientRepository.save(ingredient); + log.info("[IngredientService] modIngredient name = " + ingredient.getName() + ", operator = " + operator); + } + + public Map getByKeys() { + return ingredientRepository.findKeyNameMap().stream().collect(Collectors.toMap(Ingredient::getKey, Ingredient::getName)); + } + + public List getByKeys(List keys) { + return ingredientRepository.findByKeyInOrderByKeyAsc(keys); + } + + public List getByKeyword(String keyword) { + return ingredientRepository.findByKeyStartsWithOrNameStartsWithOrderByKeyAsc(keyword); + } + + public List getFullByKeys(Collection keys) { + return ingredientRepository.findByKeyIn(keys); + } + + public Page queryIngredientByMark(Long vender, String mark, String key, String type, PageRequest pageRequest) { + if (vender <= 0) { + if (StringUtils.isBlank(key)) { + return StringUtils.isBlank(type) ? ingredientRepository.findAll(pageRequest) : ingredientRepository.findByTypeOrderByIdDesc(type, pageRequest); + } + if (StringUtils.isNumeric(key)) { + return StringUtils.isBlank(type) ? ingredientRepository.findByKeyStartsWithOrderByIdDesc(key, pageRequest) : ingredientRepository.findByTypeAndKeyStartsWithOrderByIdDesc(type, key, pageRequest); + } else { + return StringUtils.isBlank(type) ? ingredientRepository.findByNameStartsWithOrderByIdDesc(key, pageRequest) : ingredientRepository.findByTypeAndNameStartsWithOrderByIdDesc(type, key, pageRequest); + } + } else { + return ingredientDTORepository.findByVenderAndTypeAndMarkAndKey(vender, type, mark, key, pageRequest); + } + } + + public Ingredient queryIngredientByKey(String key) { + return ingredientRepository.findByKey(key); + } + + public boolean existsIngredientByKey(String key) { + return ingredientRepository.existsByKey(key); + } + + @Transactional + public void addMark(MarkType mark, String ingredient, Long vender, String operator) { + if (ingredientMarkRepository.updateMarkAndOperateByVenderAndIngredient(mark, operator, vender, ingredient) <= 0) { + Instant dateTime = Instant.now(); + ingredientMarkRepository.save(IngredientMark.builder().mark(mark).ingredient(ingredient).vender(vender).created(dateTime).modify(dateTime).build()); + } + log.info("[IngredientService] addMark ingredient = " + ingredient + ", mark = " + mark + ", operator = " + operator); + } + + public void delMark(String ingredient, Long vender, String operator) { + ingredientMarkRepository.deleteByVenderAndIngredient(vender, ingredient); + log.info("[IngredientService] delMark ingredient = " + ingredient + ", operator = " + operator); + } + + public void delMark(Long vender, String operator) { + ingredientMarkRepository.deleteByVender(vender); + log.info("[IngredientService] delMark vender = " + vender + ", operator = " + operator); + } +} \ No newline at end of file diff --git a/diet-core/src/main/java/com/mathvision/diet/service/MenuDishService.java b/diet-core/src/main/java/com/mathvision/diet/service/MenuDishService.java new file mode 100644 index 0000000..d5afa82 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/MenuDishService.java @@ -0,0 +1,329 @@ +package com.mathvision.diet.service; + +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.support.ExcelTypeEnum; +import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy; +import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Range; +import com.mathvision.diet.domian.MealType; +import com.mathvision.diet.domian.MenuDishItemDTO; +import com.mathvision.diet.entity.*; +import com.mathvision.diet.repository.MenuDishRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Slf4j +@Service +public class MenuDishService { + + @Resource + EnumService enumService; + + @Resource + NutritionService nutritionService; + + @Resource + MenuDishRepository menuDishRepository; + + @Resource + IngredientService ingredientService; + + public MenuDish add(MenuDish menuDish) { + return menuDishRepository.save(menuDish); + } + + @Transactional + public List addAll(List menuDishes) { + menuDishes.forEach(menuDish -> deleteAll(menuDish.getMenu())); + return menuDishRepository.saveAll(menuDishes); + } + + public void deleteAll(Long menuId) { + menuDishRepository.deleteByMenu(menuId); + } + + public void delete(Long menuDishId) { + menuDishRepository.deleteById(menuDishId); + } + + public MenuDish get(Long id) { + return menuDishRepository.findById(id).orElse(null); + } + + public MenuDish get(Long id, Long venderId) { + return menuDishRepository.findByIdAndVender(id, venderId); + } + + public List query(Long menuId) { + return menuDishRepository.findByMenu(menuId); + } + + public List query(Long menuId, Long vender) { + return menuDishRepository.findByMenuAndVender(menuId, vender); + } + + public List query(Long menuId, Long vender, Long day) { + return menuDishRepository.findByMenuAndVenderAndDay(menuId, vender, day); + } + + public JSONObject assess(Menu menu, long day, String crow, List dishes) { + Nutrition nutrition = nutritionService.get(menu.getNutrient()); + BigDecimal overflow = nutrition.getOverflow(); + Map> itemStandard = nutrition.getIngredient().getOrDefault(crow, new HashMap<>()); + Map ingredientMap = ingredientService.getFullByKeys(dishes.stream().filter(x -> CollectionUtils.isNotEmpty(x.getIngredient())).flatMap(x -> x.getIngredient().stream().map(MenuDishItemDTO::getKey)).collect(Collectors.toSet())).stream().collect(Collectors.toMap(Ingredient::getKey, v -> v)); + Map items = dishes.stream().flatMap(dish -> dish.getIngredient().stream().filter(item -> item.getValue().containsKey(crow) && item.getValue().get(crow).doubleValue() > 0 && ingredientMap.containsKey(item.getKey()))).flatMap(x -> ingredientMap.get(x.getKey()).getNutrient().entrySet().stream().map(n -> Pair.of(n.getKey(), n.getValue().multiply(x.getValue().get(crow)).divide(new BigDecimal(100), RoundingMode.HALF_UP)))).collect(Collectors.toMap(Pair::getKey, Pair::getValue, BigDecimal::add)); + Map types = dishes.stream().flatMap(dish -> dish.getIngredient().stream().filter(item -> ingredientMap.containsKey(item.getKey())).map(item -> ingredientMap.get(item.getKey()).getType())).collect(Collectors.toMap(x -> x, x -> 1, Integer::sum)); + + JSONObject result = new JSONObject(); + result.put("day", day); + result.put("crow", crow); + result.put("meals", dishes.stream().map(MenuDish::getMeal).collect(Collectors.toSet())); + result.put("types", types); + + JSONArray contents = new JSONArray(); + items.forEach((key, value) -> { + FoodNutrient foodNutrient = enumService.getNutrient(key); + + if (foodNutrient == null) { + return; + } + JSONObject content = new JSONObject(); + content.put("nutrition", String.format("%s/%s", foodNutrient.getValue(), foodNutrient.getMeasurement())); + content.put("virtual", value); + + Map standard = itemStandard.get(key); + BigDecimal max = standard == null ? null : standard.get("max"); + BigDecimal min = standard == null ? null : standard.get("min"); + BigDecimal ul = standard == null ? null : standard.get("ul"); + if (max == null) { + if (min == null) { + content.put("standard", "-"); + content.put("ul", ul == null ? "-" : ul.toString()); + content.put("overload", "-"); + content.put("conclusion", "-"); + } else { + content.put("standard", "≥" + min); + content.put("ul", ul == null ? "-" : ul.toString()); + if (min.compareTo(value) > 0) { + content.put("overload", value.subtract(min).divide(min, RoundingMode.FLOOR)); + if(min.subtract(value).compareTo(overflow.divide(new BigDecimal(100), RoundingMode.FLOOR).multiply(value)) > 0) { + content.put("conclusion", "不足"); + } else { + content.put("conclusion", "适量"); + } + } else { + content.put("overload", BigDecimal.ZERO); + content.put("conclusion", "适量"); + } + } + } else { + if (min == null) { + content.put("standard", "≤" + max); + content.put("ul", ul == null ? "-" : ul.toString()); + if (max.compareTo(value) < 0) { + if(value.subtract(max).compareTo(overflow.divide(new BigDecimal(100), RoundingMode.FLOOR).multiply(value)) > 0) { + if(ul != null && value.compareTo(ul) > 0) { + content.put("conclusion", "严重超标"); + } else { + content.put("conclusion", "超量"); + } + } else { + content.put("conclusion", "适量"); + } + content.put("overload", value.subtract(max).divide(max, RoundingMode.FLOOR)); + } else { + content.put("overload", BigDecimal.ZERO); + } + } else { + content.put("standard", min + "~" + max); + content.put("ul", ul == null ? "-" : ul.toString()); + if (min.compareTo(value) > 0) { + content.put("overload", value.subtract(min).divide(min, RoundingMode.FLOOR)); + if(min.subtract(value).compareTo(overflow.divide(new BigDecimal(100), RoundingMode.FLOOR).multiply(value)) > 0) { + content.put("conclusion", "不足"); + } else { + content.put("conclusion", "适量"); + } + } else { + if (max.compareTo(value) < 0) { + content.put("overload", value.subtract(max).divide(max, RoundingMode.FLOOR)); + if(value.subtract(max).compareTo(overflow.divide(new BigDecimal(100), RoundingMode.FLOOR).multiply(value)) > 0) { + if(ul != null && value.compareTo(ul) > 0) { + content.put("conclusion", "严重超标"); + } else { + content.put("conclusion", "超量"); + } + } else { + content.put("conclusion", "适量"); + } + } else { + content.put("overload", "0%"); + } + } + } + } + contents.add(content); + }); + result.put("ingredient", contents); + return result; + } + + public JSONObject types(Menu menu, List dishes) { + Nutrition nutrition = nutritionService.get(menu.getNutrient()); + Map dayStandard = nutrition.getFoodCategoryDay(); + Map weekStandard = nutrition.getFoodCategoryWeek(); + Map ingredientMap = ingredientService.getFullByKeys(dishes.stream().filter(x -> CollectionUtils.isNotEmpty(x.getIngredient())).flatMap(x -> x.getIngredient().stream().map(MenuDishItemDTO::getKey)).collect(Collectors.toSet())).stream().collect(Collectors.toMap(Ingredient::getKey, v -> v)); + Map, Integer> typesDay = dishes.stream().flatMap(dish -> dish.getIngredient().stream().filter(item -> ingredientMap.containsKey(item.getKey())).map(item -> Pair.of(dish.getDay(), ingredientMap.get(item.getKey()).getType()))).collect(Collectors.toMap(x -> x, x -> 1, Integer::sum)); + JSONArray dayRule = new JSONArray(); + Map dayMaps = Maps.newHashMap(); + typesDay.forEach((key, num) -> { + Long day = key.getKey(); + JSONArray dayCollection = dayMaps.getOrDefault(day, new JSONArray()); + String type = key.getValue(); + int standard = dayStandard.getOrDefault(type, BigDecimal.ZERO).intValue(); + JSONObject content = new JSONObject(); + content.put("day", day); + content.put("name", type); + content.put("standard", standard); + content.put("supplied", num); + content.put("lack", num >= standard ? 0 : standard - num); + dayCollection.add(content); + dayMaps.put(day, dayCollection); + }); + dayRule.addAll(dayMaps.values()); + + Map typesAll = dishes.stream().flatMap(dish -> dish.getIngredient().stream().filter(item -> ingredientMap.containsKey(item.getKey())).map(item -> ingredientMap.get(item.getKey()).getType())).collect(Collectors.toMap(x -> x, x -> 1, Integer::sum)); + JSONArray weekRule = new JSONArray(); + typesAll.forEach((type, num) -> { + BigDecimal standard = weekStandard.getOrDefault(type, BigDecimal.ZERO); + JSONObject content = new JSONObject(); + content.put("name", type); + content.put("standard", standard); + content.put("supplied", num); + content.put("lack", num.compareTo(standard.intValue()) > 0 ? 0 : standard.intValue() - num); + weekRule.add(content); + }); + + JSONObject result = new JSONObject(); + result.put("dayRule", dayRule); + result.put("weekRule", weekRule); + return result; + } + + public JSONObject energy(long day, String crow, List dishes) { + List allConcerned= Lists.newArrayList("energy", "protein", "fat", "carbs"); + Map ingredientMap = ingredientService.getFullByKeys(dishes.stream().filter(x -> CollectionUtils.isNotEmpty(x.getIngredient())).flatMap(x -> x.getIngredient().stream().map(MenuDishItemDTO::getKey)).collect(Collectors.toSet())).stream().collect(Collectors.toMap(Ingredient::getKey, v -> v)); + Map items = dishes.stream() + .flatMap(dish -> dish.getIngredient().stream().filter(item -> item.getValue().containsKey(crow) && item.getValue().get(crow).doubleValue() > 0 && ingredientMap.containsKey(item.getKey()))) + .flatMap(x -> ingredientMap.get(x.getKey()).getNutrient().entrySet().stream().map(n -> Pair.of(n.getKey(), n.getValue().multiply(x.getValue().get(crow)).divide(new BigDecimal(100), RoundingMode.HALF_UP)))) + .filter(x -> allConcerned.contains(x.getKey())) + .collect(Collectors.toMap(Pair::getKey, Pair::getValue, BigDecimal::add)); + + JSONObject result = new JSONObject(); + result.put("day", day); + result.put("crow", crow); + result.put("meals", dishes.stream().map(MenuDish::getMeal).collect(Collectors.toSet())); + + BigDecimal energy = items.get("energy"); + if (energy == null || energy.doubleValue() <= 0) { + return result; + } + JSONArray contents = new JSONArray(); + contents.add(toEnergyContent(energy, "protein", Range.closed(10, 20), items, ingredientMap)); + contents.add(toEnergyContent(energy, "fat", Range.closed(20, 30), items, ingredientMap)); + contents.add(toEnergyContent(energy, "carbs", Range.closed(50, 60), items, ingredientMap)); + result.put("energy", contents); + return result; + } + + private JSONObject toEnergyContent(BigDecimal energy, String key, Range standard, Map items, Map ingredientMap) { + double value = items.getOrDefault(key, BigDecimal.ZERO).divide(energy, RoundingMode.FLOOR).multiply(new BigDecimal(100)).doubleValue(); + JSONObject result = JSONObject.of("name", enumService.getNutrient(key).getValue() + "/总能量"); + result.put("standard", String.format("%s~%s", standard.lowerEndpoint(), standard.upperEndpoint())); + result.put("value", value); + result.put("conclusion", standard.lowerEndpoint() > value ? "略低" : standard.upperEndpoint() < value ? "略高" : "合适"); + return result; + } + + public void export(Menu menu, OutputStream outputStream) { + Map>> menuDishes = menuDishRepository.findByMenu(menu.getId()).stream().collect(Collectors.groupingBy(MenuDish::getMeal, Collectors.groupingBy(MenuDish::getDay))); + List allMeals = new ArrayList<>(menuDishes.keySet()).stream().sorted(Comparator.comparing(MealType::toType)).collect(Collectors.toList()); + List allDays = menuDishes.entrySet().stream().flatMap(kv -> kv.getValue().keySet().stream()).distinct().sorted().collect(Collectors.toList()); + List allCrows = menu.getCrows(); + if (CollectionUtils.isEmpty(allDays)) { + allDays = IntStream.rangeClosed(1, Math.toIntExact(menu.getDay())).mapToObj(Long::valueOf).collect(Collectors.toList()); + } + + List> headers = Lists.newArrayList(); + headers.add(Lists.newArrayList("")); + allDays.forEach(day -> { + headers.add(Lists.newArrayList(String.format("第%s天", day), "菜品名称")); + headers.add(Lists.newArrayList(String.format("第%s天", day), "食材名称")); + allCrows.forEach(crow -> headers.add(Lists.newArrayList(String.format("第%s天", day), crow))); + }); + + Map keyName = ingredientService.getByKeys(); + List> contents = Lists.newArrayList(); + List finalAllDays = allDays; + allMeals.forEach(meal -> { + int maxDishes = menuDishes.get(meal).values().stream().map(List::size).max(Comparator.naturalOrder()).orElse(0); + if (maxDishes <= 0) return; + IntStream.rangeClosed(1, maxDishes).forEach(dishIndex -> { + List dishes = finalAllDays.stream().map(day -> { + List dish = menuDishes.get(meal).get(day); + if (dish == null || dish.size() < dishIndex) { + return MenuDish.builder().meal(meal).day(day).ingredient(Lists.newArrayList()).build(); + } else { + return dish.get(dishIndex -1); + } + }).collect(Collectors.toList()); + + int maxItems = dishes.stream().map(item -> item.getIngredient().size()).max(Comparator.naturalOrder()).orElse(0); + if (maxItems <= 0) return; + IntStream.rangeClosed(1, maxItems).forEach(itemIndex -> { + List> items = dishes.stream().map(dish -> { + List item = dish.getIngredient(); + if (item == null || item.size() < itemIndex) { + return Pair.of(dish.getName(), MenuDishItemDTO.builder().value(Maps.newHashMap()).build()); + } else { + return Pair.of(dish.getName(), item.get(itemIndex - 1)); + } + }).collect(Collectors.toList()); + + List content = Lists.newArrayList(); + content.add(meal); + items.forEach(item -> { + content.add(item.getKey()); + content.add(keyName.getOrDefault(item.getValue().getKey(), item.getValue().getKey())); + allCrows.forEach(crow -> { + BigDecimal value = item.getValue().getValue().get(crow); + if (value != null) { + content.add(value); + } + }); + }); + contents.add(content); + }); + }); + }); + EasyExcel.write(outputStream).head(headers) + .registerWriteHandler(new SimpleColumnWidthStyleStrategy(25)) + .registerWriteHandler(new SimpleRowHeightStyleStrategy((short)30,(short)20)) + .excelType(ExcelTypeEnum.XLSX).sheet(menu.getName()).doWrite(contents); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/MenuReleaseService.java b/diet-core/src/main/java/com/mathvision/diet/service/MenuReleaseService.java new file mode 100644 index 0000000..03bca4a --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/MenuReleaseService.java @@ -0,0 +1,78 @@ +package com.mathvision.diet.service; + +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.repository.MenuRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class MenuReleaseService { + @Resource + private MenuRepository menuRepository; + + + @PostConstruct + @Scheduled(cron = "0 0 0/6 * * *") + public void init() { + menuRepository.scanExpired(); + } + + public void publish(Long id, Map scale, Date startTime, Date endTime, String operator) { + menuRepository.updateStatusAndScaleAndStartTimeAndEndTimeById(MenuStatus.publish, scale, startTime.toInstant(), endTime.toInstant(), id); + log.info("[MenuReleaseService] publish id = " + id + ", operator = " + operator); + } + + public void cancel(Long id, String operator) { + menuRepository.updateStatusAndStartTimeAndEndTimeAndOperateById(MenuStatus.pass, null, null, operator, id); + log.info("[MenuReleaseService] cancel id = " + id + ", operator = " + operator); + } + + public Page list(Long vender, String name, Date startTime, Date endTime, PageRequest pageRequest) { + return menuRepository.findAll(toSpecification(vender, name, startTime, endTime), pageRequest); + } + + private Specification toSpecification(Long vender, String name, Date startTime, Date endTime) { + return (root, query, builder) -> { + List predicates = new ArrayList<>(); + + if (vender != null && vender > 0) { + predicates.add(builder.equal(root.get("vender"), vender)); + } + + predicates.add(builder.equal(root.get("status"), MenuStatus.publish)); + + if (startTime != null) { + predicates.add(builder.greaterThanOrEqualTo(root.get("modify"), startTime.toInstant())); + } + + if (endTime != null) { + predicates.add(builder.lessThanOrEqualTo(root.get("modify"), endTime.toInstant())); + } + + if (StringUtils.isNotBlank(name)) { + predicates.add(builder.like(root.get("name"), name + "%")); + } + + if (predicates.size() > 1) { + return builder.and(predicates.toArray(new Predicate[0])); + } else { + return predicates.get(0); + } + }; + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/MenuReviewService.java b/diet-core/src/main/java/com/mathvision/diet/service/MenuReviewService.java new file mode 100644 index 0000000..bf85331 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/MenuReviewService.java @@ -0,0 +1,105 @@ +package com.mathvision.diet.service; + +import com.google.common.collect.Lists; +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.entity.MenuApprove; +import com.mathvision.diet.repository.MenuApproveRepository; +import com.mathvision.diet.repository.MenuRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Predicate; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +@Slf4j +@Service +public class MenuReviewService { + private static final List SUPPORTED_STATUS = Lists.newArrayList(MenuStatus.submit, MenuStatus.pass, MenuStatus.reject); + @Resource + private MenuRepository menuRepository; + + @Resource + private MenuApproveRepository menuApproveRepository; + + @PostConstruct + @Scheduled(cron = "0 0 0/6 * * *") + public void init() { + menuApproveRepository.scanExpired(); + } + + public void submit(Long id, String operator) { + menuRepository.updateStatusAndOperateById(MenuStatus.submit, operator, id); + } + + public void pass(Long id, String reason, String operator) { + Instant datetime = Instant.now(); + menuRepository.updateStatusAndApproveAndOperateById(MenuStatus.pass, reason, operator, id); + menuApproveRepository.save(MenuApprove.builder().menu(id).pass(true).approve(reason).operate(operator).created(datetime).modify(datetime).build()); + } + + public void reject(Long id, String reason, String operator) { + Instant datetime = Instant.now(); + menuRepository.updateStatusAndApproveAndOperateById(MenuStatus.reject, reason, operator, id); + menuApproveRepository.save(MenuApprove.builder().menu(id).pass(false).approve(reason).operate(operator).created(datetime).modify(datetime).build()); + } + + public void disable(Long id, String operator) { + menuRepository.updateStatusAndOperateById(MenuStatus.disabled, operator, id); + } + + public Object count() { + return menuRepository.count(Lists.newArrayList(MenuStatus.submit, MenuStatus.pass, MenuStatus.reject)); + } + + public Page list(Long vender, MenuStatus status, String name, Date startTime, Date endTime, PageRequest pageRequest) { + return menuRepository.findAll(toSpecification(vender, status, name, startTime, endTime), pageRequest); + } + + private Specification toSpecification(Long vender, MenuStatus status, String name, Date startTime, Date endTime) { + return (root, query, builder) -> { + List predicates = new ArrayList<>(); + + if (vender != null && vender > 0) { + predicates.add(builder.equal(root.get("vender"), vender)); + } + + if( status != null && SUPPORTED_STATUS.contains(status)) { + predicates.add(builder.equal(root.get("status"), status)); + } else { + CriteriaBuilder.In in = builder.in(root.get("status")); + Lists.newArrayList(MenuStatus.submit, MenuStatus.pass, MenuStatus.reject).forEach(in::value); + predicates.add(in); + } + + if (startTime != null) { + predicates.add(builder.greaterThanOrEqualTo(root.get("modify"), startTime.toInstant())); + } + + if (endTime != null) { + predicates.add(builder.lessThanOrEqualTo(root.get("modify"), endTime.toInstant())); + } + + if (StringUtils.isNotBlank(name)) { + predicates.add(builder.like(root.get("name"), name + "%")); + } + + if (predicates.size() > 1) { + return builder.and(predicates.toArray(new Predicate[0])); + } else { + return predicates.get(0); + } + }; + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/MenuService.java b/diet-core/src/main/java/com/mathvision/diet/service/MenuService.java new file mode 100644 index 0000000..ec22969 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/MenuService.java @@ -0,0 +1,125 @@ +package com.mathvision.diet.service; + +import com.mathvision.diet.domian.MenuDishItemDTO; +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.repository.MenuDishRepository; +import com.mathvision.diet.repository.MenuRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import javax.persistence.criteria.Predicate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class MenuService { + @Resource + private MenuRepository menuRepository; + + @Resource + private MenuDishRepository menuDishRepository; + + public List add(List menuList) { + return menuRepository.saveAll(menuList); + } + + @Transactional + public void delete(Long id, Long vender, String operator) { + if (vender == null || vender <= 0) { + menuRepository.deleteById(id); + menuDishRepository.deleteByMenu(id); + } else { + menuRepository.deleteByIdAndVender(id, vender); + menuDishRepository.deleteByMenuAndVender(id, vender); + } + log.info("[MenuService] delete id = " + id + ", vender = " + vender + ", operator = " + operator); + } + + @Transactional + public void delete(Long vender, String operator) { + menuRepository.deleteByVender(vender); + menuDishRepository.deleteByVender(vender); + log.info("[MenuService] delete vender = " + vender + ", operator = " + operator); + } + + public void update(Menu menu) { + menuDishRepository.deleteByMenuAndDayGreaterThanAndMealNotIn(menu.getId(), menu.getDay(), menu.getMeals()); + menuDishRepository.findByMenu(menu.getId()).forEach(x -> { + List ingredient = x.getIngredient(); + if (CollectionUtils.isNotEmpty(ingredient)) { + ingredient.forEach(i -> i.setValue(i.getValue().entrySet().stream().filter(crow -> menu.getCrows().contains(crow.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)))); + x.setOperate(menu.getOperate()); + x.setModify(menu.getModify()); + menuDishRepository.save(x); + } + }); + menuRepository.save(menu); + log.info("[MenuService] update id = " + menu.getId() + ", vender = " + menu.getVender() + ", operator = " + menu.getOperate()); + } + + public Menu get(Long id) { + return menuRepository.findById(id).orElse(null); + } + + public Menu get(Long id, Long vender) { + return menuRepository.findByIdAndVender(id, vender); + } + + public List getByNutrition(Long nutrition) { + return menuRepository.findByNutrient(nutrition); + } + + public Long current(Long vender) { + return menuRepository.findCurrentMenu(vender, MenuStatus.publish.getCode()); + } + + public Page list(Long vender, String name, MenuStatus status, Date startTime, Date endTime, PageRequest pageRequest) { + return menuRepository.findAll(toSpecification(vender, name, status, startTime, endTime), pageRequest); + } + + private Specification toSpecification(Long vender, String name, MenuStatus status, Date startTime, Date endTime) { + return (root, query, builder) -> { + List predicates = new ArrayList<>(); + + if (vender != null && vender > 0) { + predicates.add(builder.equal(root.get("vender"), vender)); + } + + if (status == null || MenuStatus.publish.equals(status)) { + predicates.add(builder.notEqual(root.get("status"), MenuStatus.publish.getCode())); + } else { + predicates.add(builder.equal(root.get("status"), status)); + } + + if (startTime != null) { + predicates.add(builder.greaterThanOrEqualTo(root.get("modify"), startTime.toInstant())); + } + + if (endTime != null) { + predicates.add(builder.lessThanOrEqualTo(root.get("modify"), endTime.toInstant())); + } + + if (StringUtils.isNotBlank(name)) { + predicates.add(builder.like(root.get("name"), name + "%")); + } + + if (predicates.size() > 1) { + return builder.and(predicates.toArray(new Predicate[0])); + } else { + return predicates.get(0); + } + }; + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/NutritionService.java b/diet-core/src/main/java/com/mathvision/diet/service/NutritionService.java new file mode 100644 index 0000000..96f3204 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/NutritionService.java @@ -0,0 +1,92 @@ +package com.mathvision.diet.service; + +import com.mathvision.diet.entity.Nutrition; +import com.mathvision.diet.repository.NutritionRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import javax.annotation.Resource; +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class NutritionService { + + @Resource + private EnumService enumService; + + @Resource + private VenderService venderService; + + @Resource + private MenuService menuService; + + @Resource + private NutritionRepository nutritionRepository; + + public Nutrition add(Nutrition nutrition, String operator) { + check(nutrition, operator); + log.info("[NutritionService] add name = " + nutrition.getName() + ", operator = " + operator); + return nutritionRepository.save(nutrition); + } + + public void delete(long id, String operator) { + Assert.isTrue(CollectionUtils.isEmpty(menuService.getByNutrition(id)), "[参数错误]该营养计划使用中不可删除!"); + nutritionRepository.deleteById(id); + log.info("[NutritionService] delete id = " + id + ", operator = " + operator); + } + + public Nutrition update(Nutrition nutrition, String operator) { + check(nutrition, operator); + Assert.isTrue(CollectionUtils.isEmpty(menuService.getByNutrition(nutrition.getId())), "[参数错误]该营养计划使用中不可删除!"); + log.info("[NutritionService] update name = " + nutrition.getName() + ", operator = " + operator); + return nutritionRepository.save(nutrition); + } + + public boolean exists(Long id) { + return nutritionRepository.existsById(id); + } + + public boolean notExists(String name) { + return !nutritionRepository.existsByName(name); + } + + public Nutrition get(Long id) { + return nutritionRepository.findById(id).orElse(null); + } + + public Nutrition get(String name) { + return nutritionRepository.findByName(name); + } + + public List query(Long vender, String name) { + return nutritionRepository.findAll().stream().filter(x -> (StringUtils.isBlank(name) || x.getName().contains(name)) && (vender == null || vender ==0 || x.getVendors().contains(vender))).collect(Collectors.toList()); + } + + public Page list(String name, PageRequest pageRequest) { + return StringUtils.isBlank(name) ? nutritionRepository.findAll(pageRequest) : nutritionRepository.findByNameStartsWithOrderByIdDesc(name, pageRequest); + } + + private void check(Nutrition nutrition, String operator) { + nutrition.setVendors(nutrition.getVendors().stream().filter(x -> venderService.exists(x)).collect(Collectors.toList())); + Assert.isTrue(StringUtils.isNotBlank(nutrition.getName()) && + CollectionUtils.isNotEmpty(nutrition.getVendors()) && + nutrition.getOverflow() != null && + (nutrition.getFoodCategoryDay() == null || nutrition.getFoodCategoryDay().keySet().stream().allMatch(x -> enumService.checkCategory(x))) && + (nutrition.getFoodCategoryWeek() == null || nutrition.getFoodCategoryWeek().keySet().stream().allMatch(x -> enumService.checkCategory(x))) && + (nutrition.getIngredient() == null || nutrition.getIngredient().entrySet().stream().allMatch(x -> x.getValue().keySet().stream().allMatch(n -> enumService.checkNutrient(n)))) + , "[参数错误]必填参数未填写,或者单位类别为空、类型和营养素不在取值范围内!"); + + Instant dateTime = Instant.now(); + nutrition.setOperate(operator); + nutrition.setCreated(dateTime); + nutrition.setModify(dateTime); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/UserService.java b/diet-core/src/main/java/com/mathvision/diet/service/UserService.java new file mode 100644 index 0000000..76678cc --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/UserService.java @@ -0,0 +1,275 @@ +package com.mathvision.diet.service; + +import com.alibaba.fastjson2.JSON; +import com.google.common.collect.Lists; +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.domian.AuthType; +import com.mathvision.diet.domian.ClientType; +import com.mathvision.diet.domian.RoleType; +import com.mathvision.diet.entity.*; +import com.mathvision.diet.repository.*; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class UserService { + + @Resource + private EnumService enumService; + + @Resource + private UserRepository userRepository; + + @Resource + private UserLogRepository userLogRepository; + + @Resource + private UserSessionRepository userSessionRepository; + + @Resource + private UserMessageRepository userMessageRepository; + + @Resource + private UserRoleRepository userRoleRepository; + + @Resource + private RoleRepository roleRepository; + + @Resource + private VenderRepository venderRepository; + + @PostConstruct + @Scheduled(cron = "0 0 0/6 * * *") + public void init() { + userLogRepository.scanExpired(); + userMessageRepository.scanExpired(); + userSessionRepository.scanExpired(); + } + + public UserDO login(String uid, String password, ClientType clientType, String clientVersion) { + Assert.isTrue(checkUser(uid, password), "[参数错误]用户名密码错误!"); + userLogRepository.save(UserLog.builder().uid(uid).clientType(clientType).clientVersion(clientVersion).login(Instant.now()).build()); + log.info("[UserService] login uid = " + uid + ", clientType = " + clientType + ", clientVersion = " + clientVersion); + return queryUser(uid); + } + + @Transactional + public void addUser(String uid, String name, String password, Long roleId, Long venderId, String operator) { + Assert.isTrue(checkUser(uid), "[参数错误]用户名重复!"); + Assert.isTrue(StringUtils.isNotBlank(name), "[参数错误]姓名必填!"); + Assert.isTrue(StringUtils.isNotBlank(password) && password.length() == 16, "[参数错误]密码为空, 或者密码长度错误!"); + Assert.isTrue(checkRole(roleId, venderId), "[参数错误]未赋初始角色,或者该角色错误!"); + + Instant dateTime = Instant.now(); + User user = User.builder().uid(uid).name(name).pwd(password).status(true).created(dateTime).modify(dateTime).build(); + UserRole userRole = UserRole.builder().roleId(roleId).uid(uid).vender(venderId).operate(operator).created(dateTime).modify(dateTime).build(); + + userRepository.save(user); + userRoleRepository.save(userRole); + log.info("[UserService] addUser venderId= " + venderId + ", uid = " + uid + ", roleId = " + roleId + ", operator = " + operator); + } + + @Transactional + public void delUser(Long venderId, String operator) { + List users = listUser(venderId).stream().map(UserDO::getUid).collect(Collectors.toList()); + if (!users.isEmpty()) { + userRepository.deleteByUidIn(users); + } + userRoleRepository.deleteByVender(venderId); + roleRepository.deleteByVender(venderId); + log.info("[UserService] delUser venderId = " + venderId + ", operator = " + operator); + } + + @Transactional + public void delUser(String uid, Long venderId, String operator) { + Assert.isTrue(checkRole(uid, venderId), "[参数错误]该用户不是您单位用户!"); + Assert.isTrue(countRole(venderId, uid) > 0, "[参数错误]至少保留一个管理员!"); + if (userRoleRepository.deleteByUidAndVender(uid, venderId) > 0) { + userRepository.deleteByUid(uid); + } + log.info("[UserService] delUser uid = " + uid + "venderId = " + venderId + ", operator = " + operator); + } + + public void changeUser(String uid, String name, String password, Long roleId, Long venderId, String operator) { + Assert.isTrue(checkRole(uid, venderId), "[参数错误]该用户不是您单位用户!"); + if (roleId != null) { + if (roleId > 0) { + Assert.isTrue(checkRole(roleId, venderId), "[参数错误]该角色不属于您单位!"); + } else { + Assert.isTrue(countRole(venderId, uid) > 0, "[参数错误]至少保留一个管理员!"); + } + userRoleRepository.updateRoleIdAndOperateByUidAndVender(roleId, operator, uid, venderId); + log.info("[UserService] changeUser uid = " + uid + ", roleId = " + roleId + ", operator = " + operator); + } + if (StringUtils.isNotBlank(name)) { + userRepository.updateNameByUid(name, uid); + log.info("[UserService] changeUser uid = " + uid + ", name = " + name + ", operator = " + operator); + } + if (StringUtils.isNotBlank(password)) { + userRepository.updatePwdByUid(password, uid); + log.info("[UserService] changeUser uid = " + uid + ", password = " + password + ", operator = " + operator); + } + } + + public UserDO queryUser(String uid) { + UserDO result = new UserDO(); + User user = userRepository.findByUidAndStatus(uid, true); + if (user == null) { + log.info("[UserService] queryUser user=null uid = " + uid); + return null; + } + result.setUid(user.getUid()); + result.setName(user.getName()); + result.setPhone(user.getPhone()); + result.setTime(user.getCreated()); + + UserRole userRole = userRoleRepository.findByUid(uid); + if (userRole == null) { + log.info("[UserService] queryUser userRole=null uid = " + uid); + return null; + } + + if (userRole.getVender() != 0) { + Vender vender = venderRepository.findByIdAndStatus(userRole.getVender(), true); + if (userRole.getVender() != 0 && vender == null) { + log.info("[UserService] queryUser vender=null or closed vender uid=" + uid + ", venderId=" + userRole.getVender()); + return null; + } + result.setVender(vender); + } + + Role role = query(userRole.getRoleId()); + if (role != null) { + result.setRoleId(role.getId()); + result.setRoleName(role.getRoleName()); + result.setRoleType(role.getRoleType().getType()); + result.setRoleItems(enumService.getRoleItems(role.getRoleItems())); + } + + return result; + } + + public List listUser(Long venderId) { + if (venderId == null || venderId < 0) { + return Lists.newArrayList(); + } + Map roles = roleRepository.findByVender(venderId).stream().collect(Collectors.toMap(Role::getId, v -> v)); + Map userRole = userRoleRepository.findByVender(venderId).stream().collect(Collectors.toMap(UserRole::getUid, v -> roles.getOrDefault(v.getRoleId(), new Role()))); + return userRepository.findByUidIn(userRole.keySet()).stream().map(user -> { + Role role = userRole.getOrDefault(user.getUid(), null); + return UserDO.builder() + .uid(user.getUid()) + .name(user.getName()) + .phone(user.getPhone()) + .time(user.getCreated()) + .roleId(role != null ? role.getId() : null) + .roleName(role != null ? role.getRoleName() : null) + .build(); + }).collect(Collectors.toList()); + } + + public List listRoleItems(AuthType itemType) { + return enumService.getRoleItems(itemType); + } + + public Role addRole(Long venderId) { + List items = listRoleItems(AuthType.CLIENT).stream().map(RoleItem::getId).collect(Collectors.toList()); + return addRole("超级管理员", items, RoleType.SYSTEM, AuthType.CLIENT, venderId, "system"); + } + + public Role addRole(String roleName, List items, RoleType roleType, AuthType itemType, Long venderId, String operator) { + List allItem = listRoleItems(itemType).stream().map(RoleItem::getId).collect(Collectors.toList()); + items = items == null ? Lists.newArrayList() : items.stream().filter(Objects::nonNull).filter(allItem::contains).collect(Collectors.toList()); + Assert.isTrue(StringUtils.isNotBlank(roleName) && venderId != null, "[参数错误]角色名称不能为空!"); + Instant dateTime = Instant.now(); + Role role = Role.builder().vender(venderId).roleType(roleType).roleName(roleName).roleItems(items).operate(operator).created(dateTime).modify(dateTime).build(); + log.info("[UserService] addRole venderId=" + venderId + ", operator=" + operator); + return roleRepository.save(role); + } + + public void delRole(Long roleId, Long venderId, String operator) { + Assert.isTrue(roleId != null && roleId > 0 && roleRepository.existsByIdAndVender(roleId, venderId) && !userRoleRepository.existsByRoleIdAndVender(roleId, venderId), "[参数错误]角色使用中,不能删除!"); + roleRepository.deleteByIdAndVenderAndRoleTypeNot(roleId, venderId, RoleType.SYSTEM); + log.info("[UserService] delRole venderId=" + venderId + ",roleId=" + roleId + ", operator=" + operator); + } + + public void changeRole(Long roleId, String roleName, List items, AuthType itemType, Long venderId, String operator) { + Assert.isTrue(roleId != null && roleId > 0 && roleRepository.existsByIdAndVender(roleId, venderId), "[参数错误]该角色不属于您!"); + if(StringUtils.isNotBlank(roleName)) { + roleRepository.updateRoleNameAndOperateByIdAndVenderAndRoleTypeNot(roleName, operator, roleId, venderId); + log.info("[UserService] changeRole venderId=" + venderId + ", roleName=" + roleName + ", roleId=" + roleId + ", operator=" + operator); + } + + List allItem = listRoleItems(itemType).stream().map(RoleItem::getId).collect(Collectors.toList()); + items = items == null ? Lists.newArrayList() : items.stream().filter(Objects::nonNull).filter(allItem::contains).collect(Collectors.toList()); + if(!items.isEmpty()) { + roleRepository.updateRoleItemsAndOperateByIdAndVenderAndRoleTypeNot(JSON.toJSONString(items), operator, roleId, venderId); + log.info("[UserService] changeRole venderId=" + venderId + ", allItem=" + JSON.toJSONString(items) + ", roleId=" + roleId + ", operator=" + operator); + } + } + + public List listRole(Long venderId) { + return roleRepository.findByVender(venderId); + } + + public Role query(Long roleId) { + return roleId != null && roleId > 0 ? roleRepository.findById(roleId).orElse(null) : null; + } + + public boolean checkUser(String uid, String password) { + return StringUtils.isNotBlank(uid) && StringUtils.isNotBlank(password) && userRepository.existsByUidAndPwdAndStatus(uid, password, true); + } + + /** + * 查看用户是否存在 + */ + public boolean checkUser(String uid) { + return StringUtils.isBlank(uid) || !userRepository.existsByUid(uid); + } + + /** + * 查看用户属于该单位 + */ + public boolean checkRole(String uid, Long venderId) { + return StringUtils.isNotBlank(uid) && venderId != null && userRoleRepository.existsByUidAndVender(uid, venderId); + } + + /** + * 检查权限属于该单位 + */ + public boolean checkRole(Long roleId, Long venderId) { + return roleId != null && roleId > 0 && venderId != null && roleRepository.existsByIdAndVender(roleId, venderId); + } + + /** + * 统计管理员数量 + */ + public long countRole(Long venderId, String uid) { + if (venderId == null || venderId < 0) { + return 0; + } + Role role = roleRepository.findByVenderForDefaultRole(venderId, RoleType.SYSTEM); + return role == null ? 0 : userRoleRepository.countByRoleIdAndVenderAndUidNot(role.getId(), venderId, uid); + } + + public void logout(String uid) { + if (StringUtils.isBlank(uid)) { + return; + } + userLogRepository.updateLogoutByUid(Instant.now(), uid); + log.info("[UserService] logout uid=" + uid); + } +} diff --git a/diet-core/src/main/java/com/mathvision/diet/service/VenderService.java b/diet-core/src/main/java/com/mathvision/diet/service/VenderService.java new file mode 100644 index 0000000..b188c32 --- /dev/null +++ b/diet-core/src/main/java/com/mathvision/diet/service/VenderService.java @@ -0,0 +1,140 @@ +package com.mathvision.diet.service; + +import com.mathvision.diet.domian.VenderType; +import com.mathvision.diet.entity.Vender; +import com.mathvision.diet.entity.VenderConfig; +import com.mathvision.diet.repository.VenderConfigRepository; +import com.mathvision.diet.repository.VenderRepository; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; + +@Slf4j +@Service +public class VenderService { + @Resource + private VenderRepository venderRepository; + + @Resource + private VenderConfigRepository venderConfigRepository; + + @Resource + private UserService userService; + + @Resource + private MenuService menuService; + + @Resource + private DishService dishService; + + @Resource + private IngredientService ingredientService; + + @PostConstruct + @Scheduled(cron = "0 0 0/1 * * *") + public void init() { + venderRepository.scanExpired(); + } + + public VenderConfig queryConfig(Long venderId) { + if (venderId == null || venderId <= 0) { + log.info("[VenderService] queryConfig venderId = " + venderId); + return null; + } + return venderConfigRepository.findByVender(venderId); + } + + public void modConfig(Long venderId, BigDecimal breakfast, BigDecimal lunch, BigDecimal dinner, String operate) { + if (venderId == null || venderId <= 0) { + log.info("[VenderService] modConfig venderId = " + venderId + ", operate = " + operate); + return; + } + venderConfigRepository.updateBreakfastAndLunchAndDinnerAndOperateByVender(breakfast, lunch, dinner, operate, venderId); + log.info("[VenderService] modConfig success: venderId = " + venderId + ", operate = " + operate); + } + + public Vender queryVender(Long venderId) { + if (venderId == null || venderId <= 0) { + log.info("[VenderService] queryVender venderId = " + venderId); + return null; + } + return venderRepository.findById(venderId).orElse(null); + } + + public boolean exists(Long venderId) { + if (venderId == null || venderId <= 0) { + return false; + } + return venderRepository.existsById(venderId); + } + + public List listVender(String keyword, List vendors) { + if (CollectionUtils.isNotEmpty(vendors)) { + return venderRepository.findByIdIn(vendors); + } + return StringUtils.isNotBlank(keyword) ? venderRepository.findByStatusAndNameLikeOrderByNameDesc(true, keyword) : venderRepository.findByStatusOrderByNameAsc(true); + } + + public Page pageVender(String keyword, VenderType type, PageRequest pageRequest) { + if (type == null) { + return StringUtils.isBlank(keyword) ? venderRepository.findAll(pageRequest) : venderRepository.findByNameLikeOrderByIdDesc(keyword, pageRequest); + } else { + return StringUtils.isBlank(keyword) ? venderRepository.findByCategoryOrderByIdDesc(type, pageRequest) : venderRepository.findByCategoryAndNameLikeOrderByIdDesc(type, keyword, pageRequest); + } + } + + @Transactional + public void addVender(Vender vender, String password, String operator) { + Instant dateTime = Instant.now(); + vender.setStatus(true); + vender.setOperate(operator); + vender.setCreated(dateTime); + vender.setModify(dateTime); + Vender _vender = venderRepository.save(vender); + venderConfigRepository.save(VenderConfig.builder().vender(vender.getId()).breakfast(new BigDecimal(0)).lunch(new BigDecimal(0)).dinner(new BigDecimal(0)).operate(operator).created(dateTime).modify(dateTime).build()); + userService.addUser(_vender.getAccount(), _vender.getName(), password, userService.addRole(_vender.getId()).getId(), _vender.getId(), operator); + log.info("[VenderService] addVender venderId = " + vender.getName() + ", operator = " + operator); + } + + @Transactional + public void modVender(Vender vender, String operator) { + if (vender == null) return; + venderRepository.save(vender); + log.info("[VenderService] delVender venderId = " + vender.getId() + ", operator = " + operator); + } + + @Transactional + public void delVender(Long venderId, String operator) { + if (venderId == null || venderId <= 0) { + log.info("[VenderService] delVender venderId = " + venderId + ", operator = " + operator); + return; + } + venderRepository.deleteById(venderId); + venderConfigRepository.deleteByVender(venderId); + userService.delUser(venderId, operator); + ingredientService.delMark(venderId, operator); + dishService.delete(venderId, operator); + menuService.delete(venderId, operator); + log.info("[VenderService] delVender venderId = " + venderId + ", operator = " + operator); + } + + public boolean checkName(String venderName) { + return !venderRepository.existsByName(venderName); + } + + public boolean checkAccount(String account) { + return userService.checkUser(account); + } + +} \ No newline at end of file diff --git a/diet-dao/pom.xml b/diet-dao/pom.xml new file mode 100644 index 0000000..3686590 --- /dev/null +++ b/diet-dao/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + + com.mathvision.diet + diet + 1.0-SNAPSHOT + + diet-dao + jar + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.mysql + mysql-connector-j + runtime + + + com.vladmihalcea + hibernate-types-52 + 2.21.1 + + + com.googlecode.log4jdbc + log4jdbc + 1.2 + runtime + + + org.apache.commons + commons-compress + 1.21 + + + \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/AuthTypeConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/AuthTypeConvert.java new file mode 100644 index 0000000..2283920 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/AuthTypeConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.AuthType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class AuthTypeConvert implements AttributeConverter { + @Override + public String convertToDatabaseColumn(AuthType type) { + return type == null ? null : type.getType(); + } + + @Override + public AuthType convertToEntityAttribute(String s) { + return AuthType.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/DishItemConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/DishItemConvert.java new file mode 100644 index 0000000..1697a99 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/DishItemConvert.java @@ -0,0 +1,22 @@ +package com.mathvision.diet.convert; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.mathvision.diet.domian.DishItemDTO; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import java.util.List; + +@Converter +public class DishItemConvert implements AttributeConverter, String> { + @Override + public String convertToDatabaseColumn(List stringList) { + return JSON.toJSONString(stringList); + } + + @Override + public List convertToEntityAttribute(String s) { + return JSON.parseObject(s, new TypeReference>(){}); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/GenderTypeConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/GenderTypeConvert.java new file mode 100644 index 0000000..992d7dc --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/GenderTypeConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.GenderType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class GenderTypeConvert implements AttributeConverter { + @Override + public String convertToDatabaseColumn(GenderType type) { + return type == null ? null : type.getType(); + } + + @Override + public GenderType convertToEntityAttribute(String s) { + return GenderType.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/IngredientConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/IngredientConvert.java new file mode 100644 index 0000000..6b5ad69 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/IngredientConvert.java @@ -0,0 +1,22 @@ +package com.mathvision.diet.convert; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import java.math.BigDecimal; +import java.util.Map; + +@Converter(autoApply = true) +public class IngredientConvert implements AttributeConverter>>,String> { + @Override + public String convertToDatabaseColumn(Map>> stringMapMap) { + return JSON.toJSONString(stringMapMap); + } + + @Override + public Map>> convertToEntityAttribute(String s) { + return JSON.parseObject(s, new TypeReference>>>(){}); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/MarkTypeConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/MarkTypeConvert.java new file mode 100644 index 0000000..352e0e2 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/MarkTypeConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.MarkType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class MarkTypeConvert implements AttributeConverter { + @Override + public String convertToDatabaseColumn(MarkType type) { + return type == null ? null : type.getType(); + } + + @Override + public MarkType convertToEntityAttribute(String s) { + return MarkType.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/MenuDishConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/MenuDishConvert.java new file mode 100644 index 0000000..223b111 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/MenuDishConvert.java @@ -0,0 +1,23 @@ +package com.mathvision.diet.convert; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.mathvision.diet.domian.DishItemDTO; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; +import java.util.List; +import java.util.Map; + +@Converter +public class MenuDishConvert implements AttributeConverter>, String> { + @Override + public String convertToDatabaseColumn(Map> stringList) { + return JSON.toJSONString(stringList); + } + + @Override + public Map> convertToEntityAttribute(String s) { + return JSON.parseObject(s, new TypeReference>>(){}); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/MenuStatusConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/MenuStatusConvert.java new file mode 100644 index 0000000..685943a --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/MenuStatusConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.MenuStatus; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class MenuStatusConvert implements AttributeConverter { + @Override + public Integer convertToDatabaseColumn(MenuStatus type) { + return type == null ? null : type.getCode(); + } + + @Override + public MenuStatus convertToEntityAttribute(Integer s) { + return MenuStatus.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/RoleTypeConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/RoleTypeConvert.java new file mode 100644 index 0000000..dfbad70 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/RoleTypeConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.RoleType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class RoleTypeConvert implements AttributeConverter { + @Override + public String convertToDatabaseColumn(RoleType type) { + return type == null ? null : type.getType(); + } + + @Override + public RoleType convertToEntityAttribute(String s) { + return RoleType.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/convert/VenderTypeConvert.java b/diet-dao/src/main/java/com/mathvision/diet/convert/VenderTypeConvert.java new file mode 100644 index 0000000..7c8cbce --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/convert/VenderTypeConvert.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.convert; + +import com.mathvision.diet.domian.VenderType; + +import javax.persistence.AttributeConverter; +import javax.persistence.Converter; + +@Converter(autoApply = true) +public class VenderTypeConvert implements AttributeConverter { + @Override + public String convertToDatabaseColumn(VenderType type) { + return type == null ? null : type.getType(); + } + + @Override + public VenderType convertToEntityAttribute(String s) { + return VenderType.toType(s); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/AuthType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/AuthType.java new file mode 100644 index 0000000..2d97b2b --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/AuthType.java @@ -0,0 +1,26 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum AuthType { + SERVER("管理端"), CLIENT("业务端"); + + @Getter + private final String type; + + AuthType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static AuthType toType(String s) { + return Arrays.stream(AuthType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} + diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/ClientType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/ClientType.java new file mode 100644 index 0000000..e2e5fec --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/ClientType.java @@ -0,0 +1,5 @@ +package com.mathvision.diet.domian; + +public enum ClientType { + IOS, Android, WEB; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/DishItemDTO.java b/diet-dao/src/main/java/com/mathvision/diet/domian/DishItemDTO.java new file mode 100644 index 0000000..12322aa --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/DishItemDTO.java @@ -0,0 +1,17 @@ +package com.mathvision.diet.domian; + +import lombok.*; + +import java.math.BigDecimal; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +@Getter +@Setter +public class DishItemDTO { + private String key; + private BigDecimal value; + private Boolean isMain; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/GenderType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/GenderType.java new file mode 100644 index 0000000..a10269e --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/GenderType.java @@ -0,0 +1,25 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum GenderType { + F("女"), M("男"); + + @Getter + private final String type; + + GenderType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static GenderType toType(String s) { + return Arrays.stream(GenderType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/IngredientDTO.java b/diet-dao/src/main/java/com/mathvision/diet/domian/IngredientDTO.java new file mode 100644 index 0000000..de0dfc5 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/IngredientDTO.java @@ -0,0 +1,32 @@ +package com.mathvision.diet.domian; + +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Map; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +public class IngredientDTO { + @Id + private String key; + private String name; + private String type; + private String mark; + private Instant modify; + @Type(type = "json") + @Column(columnDefinition = "json", nullable = false) + private Map nutrient; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MarkType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MarkType.java new file mode 100644 index 0000000..1f771f5 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MarkType.java @@ -0,0 +1,25 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum MarkType { + frequent("常用"), forbidden("忌用"); + + @Getter + private final String type; + + MarkType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static MarkType toType(String s) { + return Arrays.stream(MarkType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MealType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MealType.java new file mode 100644 index 0000000..304c604 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MealType.java @@ -0,0 +1,25 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum MealType { + breakfast("早餐"), lunch("午餐"), dinner("晚餐"); + + @Getter + private final String type; + + MealType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static MealType toType(String s) { + return Arrays.stream(MealType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MenuCountDTO.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuCountDTO.java new file mode 100644 index 0000000..a644e02 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuCountDTO.java @@ -0,0 +1,30 @@ +package com.mathvision.diet.domian; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.MenuStatusConvert; +import lombok.*; + +import javax.persistence.Column; +import javax.persistence.Convert; + +@Builder +@NoArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +public class MenuCountDTO { + + public MenuCountDTO(MenuStatus status, Long count) { + setCount(count); + setStatus(status); + } + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingOrdinal }) + @Convert(converter = MenuStatusConvert.class) + @Column(name = "status") + private MenuStatus status; + + @Column(name = "count") + private Long count; +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MenuDishItemDTO.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuDishItemDTO.java new file mode 100644 index 0000000..73450e1 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuDishItemDTO.java @@ -0,0 +1,28 @@ +package com.mathvision.diet.domian; + +import lombok.*; + +import java.math.BigDecimal; +import java.util.Map; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@EqualsAndHashCode +public class MenuDishItemDTO { + /** + * 食材 + */ + String key; + + /** + * 是否主料 + */ + Boolean isMain; + /** + * 人群列表: + */ + Map value; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MenuStatus.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuStatus.java new file mode 100644 index 0000000..5dfd5ea --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MenuStatus.java @@ -0,0 +1,37 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum MenuStatus { + //0-草稿,1-提交审核,2-审核通过,3-审核失败,4-禁用,5-发布 + draft(0, "草稿"), submit(1,"审核中"), pass(2,"审核通过"), reject(3, "审核失败"), disabled(4, "禁用"), publish(5, "发布"); + + @Getter + private final int code; + @Getter + private final String type; + + MenuStatus(int code, String type) { + this.code = code; + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static MenuStatus toType(Integer s) { + return Arrays.stream(MenuStatus.values()).filter(x -> s != null && x.getCode() == s).findFirst().orElse(null); + } + + public static MenuStatus toType(Long s) { + return Arrays.stream(MenuStatus.values()).filter(x -> s != null && x.getCode() == s.intValue()).findFirst().orElse(null); + } + + public static MenuStatus toType(String s) { + return Arrays.stream(MenuStatus.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/MessageType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/MessageType.java new file mode 100644 index 0000000..9f40e45 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/MessageType.java @@ -0,0 +1,5 @@ +package com.mathvision.diet.domian; + +public enum MessageType { + CODE, NOTIFY, MESSAGE; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/RoleType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/RoleType.java new file mode 100644 index 0000000..be1abed --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/RoleType.java @@ -0,0 +1,25 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum RoleType { + SYSTEM("系统"), CUSTOM("自定义"); + + @Getter + private final String type; + + RoleType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static RoleType toType(String s) { + return Arrays.stream(RoleType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/domian/VenderType.java b/diet-dao/src/main/java/com/mathvision/diet/domian/VenderType.java new file mode 100644 index 0000000..22fb6b4 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/domian/VenderType.java @@ -0,0 +1,25 @@ +package com.mathvision.diet.domian; + +import lombok.Getter; + +import java.util.Arrays; + +public enum VenderType { + school("学校"), hospital("医院"), institution("事业单位"), other("其他"); + + @Getter + private final String type; + + VenderType(String type) { + this.type = type; + } + + @Override + public String toString() { + return type; + } + + public static VenderType toType(String s) { + return Arrays.stream(VenderType.values()).filter(x -> x.getType().equals(s)).findFirst().orElse(null); + } +} diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Dish.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Dish.java new file mode 100644 index 0000000..bbc3d6c --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Dish.java @@ -0,0 +1,69 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.DishItemConvert; +import com.mathvision.diet.domian.DishItemDTO; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "dish") +public class Dish { + + public Dish(Long id, String name, String marks, List ingredient) { + setId(id); + setName(name); + setMarks(marks); + setIngredient(ingredient); + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "name", nullable = false, length = 20) + private String name; + + @Column(name = "`vender`", nullable = false, length = 20) + private Long vender; + + @Lob + @Column(name = "icon") + private String icon; + + @Type(type = "json") + @Column(name = "month", columnDefinition = "json", nullable = false) + private List month; + + @Convert(converter = DishItemConvert.class) + @Column(name = "ingredient", columnDefinition = "json", nullable = false) + private List ingredient; + + @Column(name = "marks", nullable = false, length = 45) + private String marks; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/FoodCategory.java b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodCategory.java new file mode 100644 index 0000000..6d368d3 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodCategory.java @@ -0,0 +1,41 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "food_category") +public class FoodCategory { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "`key`", nullable = false, length = 32) + private String key; + + @Column(name = "name", nullable = false, length = 32) + private String value; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/FoodMark.java b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodMark.java new file mode 100644 index 0000000..261eb66 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodMark.java @@ -0,0 +1,41 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "food_mark") +public class FoodMark { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "`key`", nullable = false, length = 20) + private String key; + + @Column(name = "name", nullable = false, length = 20) + private String value; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/FoodNutrient.java b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodNutrient.java new file mode 100644 index 0000000..dcead3f --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/FoodNutrient.java @@ -0,0 +1,48 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "food_nutrient") +public class FoodNutrient { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "`key`", nullable = false, length = 20) + private String key; + + @Column(name = "name", nullable = false, length = 64) + private String value; + + @Column(name = "measurement", nullable = false, length = 10) + private String measurement; + + @Column(name = "nrv", nullable = false, precision = 5, scale = 2) + private BigDecimal nrv; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Ingredient.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Ingredient.java new file mode 100644 index 0000000..87978fa --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Ingredient.java @@ -0,0 +1,64 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Map; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "ingredient", schema = "diet") +public class Ingredient { + public Ingredient(String key, String name) { + setKey(key); + setName(name); + } + + public Ingredient(String key, String name, String type) { + setKey(key); + setName(name); + setType(type); + } + + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "`key`", nullable = false, length = 20) + private String key; + + @Column(name = "name", nullable = false, length = 64) + private String name; + + @Column(name = "type", nullable = false, length = 64) + private String type; + + @Type(type = "json") + @Column(name = "nutrient", columnDefinition = "json", nullable = false) + private Map nutrient; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/IngredientMark.java b/diet-dao/src/main/java/com/mathvision/diet/entity/IngredientMark.java new file mode 100644 index 0000000..6bc5daf --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/IngredientMark.java @@ -0,0 +1,50 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.MarkTypeConvert; +import com.mathvision.diet.domian.MarkType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "ingredient_mark", schema = "diet") +public class IngredientMark { + + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "ingredient", nullable = false, length = 20) + private String ingredient; + + @Column(name = "vender", nullable = false) + private Long vender; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingToString }) + @Convert(converter = MarkTypeConvert.class) + @Column(name = "mark", columnDefinition="ENUM('常用','忌用')", nullable = false) + private MarkType mark; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Menu.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Menu.java new file mode 100644 index 0000000..f21435d --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Menu.java @@ -0,0 +1,82 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.MenuStatusConvert; +import com.mathvision.diet.domian.MenuStatus; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "menu") +public class Menu { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "vender", nullable = false) + private Long vender; + + @Column(name = "nutrient", nullable = false) + private Long nutrient; + + @Column(name = "name", nullable = false, length = 20) + private String name; + + @Column(name = "day", columnDefinition = "int UNSIGNED not null") + private Long day; + + @Type(type = "json") + @Column(name = "meals", columnDefinition = "json", nullable = false) + private List meals; + + @Type(type = "json") + @Column(name = "crows", columnDefinition = "json", nullable = false) + private List crows; + + @Type(type = "json") + @Column(name = "scale", columnDefinition = "json", nullable = false) + private Map scale; + + @Type(type = "json") + @Column(name = "month", columnDefinition = "json", nullable = false) + private List month; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingOrdinal }) + @Convert(converter = MenuStatusConvert.class) + @Column(name = "status", columnDefinition = "tinyint UNSIGNED not null") + private MenuStatus status; + + @Column(name = "approve") + private String approve; + + @Column(name = "start_time") + private Instant startTime; + + @Column(name = "end_time") + private Instant endTime; + + @Column(name = "operate", length = 45) + private String operate; + + @Column(name = "created") + private Instant created; + + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/MenuApprove.java b/diet-dao/src/main/java/com/mathvision/diet/entity/MenuApprove.java new file mode 100644 index 0000000..ddbf5dd --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/MenuApprove.java @@ -0,0 +1,43 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "menu_approve") +public class MenuApprove { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "menu", nullable = false) + private Long menu; + + @Column(name = "approve", length = 16) + private String approve; + + @Column(name = "pass", nullable = false) + private Boolean pass = false; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/MenuDish.java b/diet-dao/src/main/java/com/mathvision/diet/entity/MenuDish.java new file mode 100644 index 0000000..c033333 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/MenuDish.java @@ -0,0 +1,65 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.domian.MenuDishItemDTO; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "menu_dish") +public class MenuDish { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "vender", nullable = false) + private Long vender; + + @Column(name = "menu", nullable = false) + private Long menu; + + @Column(name = "day", columnDefinition = "int UNSIGNED not null") + private Long day; + + @Column(name = "meal", nullable = false, length = 16) + private String meal; + + @Column(name = "dish", nullable = false) + private Long dish; + + @Column(name = "name", nullable = false, length = 64) + private String name; + + @Type(type = "json") + @Column(name = "ingredient", columnDefinition = "json", nullable = false) + private List ingredient; + + @Column(name = "marks", nullable = false, length = 45) + private String marks; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Nutrition.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Nutrition.java new file mode 100644 index 0000000..51c8cf7 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Nutrition.java @@ -0,0 +1,63 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.IngredientConvert; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.List; +import java.util.Map; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "nutrition", schema = "diet") +public class Nutrition { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "name", nullable = false, length = 20) + private String name; + + @Type( type = "json" ) + @Column(name = "vendors", columnDefinition = "json", nullable = false) + private List vendors; + + @Type( type = "json" ) + @Column(name = "food_category_day", columnDefinition = "json") + private Map foodCategoryDay; + + @Type( type = "json" ) + @Column(name = "food_category_week", columnDefinition = "json") + private Map foodCategoryWeek; + + @Convert(converter = IngredientConvert.class) + @Column(name = "ingredient", columnDefinition = "json") + private Map>> ingredient; + + @Column(name = "overflow", nullable = false, precision = 5, scale = 2) + private BigDecimal overflow; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Role.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Role.java new file mode 100644 index 0000000..0224a32 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Role.java @@ -0,0 +1,57 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.RoleTypeConvert; +import com.mathvision.diet.domian.RoleType; +import com.vladmihalcea.hibernate.type.json.JsonStringType; +import lombok.*; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; + +import javax.persistence.*; +import java.time.Instant; +import java.util.List; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = "role") +public class Role { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "role_name", nullable = false, length = 45) + private String roleName; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingToString }) + @Convert(converter = RoleTypeConvert.class) + @Column(name = "role_type", columnDefinition="ENUM('系统','自定义')", nullable = false) + private RoleType roleType; + + @Type(type = "json") + @Column(name = "role_items", columnDefinition = "json", nullable = false) + private List roleItems; + + @Column(name = "vender", nullable = false) + private Long vender; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/RoleItem.java b/diet-dao/src/main/java/com/mathvision/diet/entity/RoleItem.java new file mode 100644 index 0000000..2c49a36 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/RoleItem.java @@ -0,0 +1,52 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.AuthTypeConvert; +import com.mathvision.diet.domian.AuthType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "role_item") +public class RoleItem { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "item_name", nullable = false, length = 45) + private String itemName; + + @Column(name = "category", nullable = false, length = 50) + private String category; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingToString }) + @Convert(converter = AuthTypeConvert.class) + @Column(name = "item_type", columnDefinition="ENUM('管理端','业务端')", nullable = false) + private AuthType itemType; + + @JSONField(serialize = false) + @Column(name = "item_value", nullable = false) + private String itemValue; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/User.java b/diet-dao/src/main/java/com/mathvision/diet/entity/User.java new file mode 100644 index 0000000..8912c99 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/User.java @@ -0,0 +1,64 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.GenderTypeConvert; +import com.mathvision.diet.domian.GenderType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "user") +public class User { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uid", nullable = false, length = 16) + private String uid; + + @JSONField(serialize = false) + @Column(name = "pwd", nullable = false, length = 16) + private String pwd; + + @Column(name = "status", nullable = false) + private Boolean status = false; + + @Column(name = "name", nullable = false, length = 64) + private String name; + + @Column(name = "phone", length = 16) + private String phone; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingToString }) + @Convert(converter = GenderTypeConvert.class) + @Column(name = "gender", columnDefinition="ENUM('男','女')") + private GenderType gender; + + @Column(name = "email", length = 64) + private String email; + + @Column(name = "address", length = 64) + private String address; + + @Column(name = "flag") + private Boolean flag; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/UserLog.java b/diet-dao/src/main/java/com/mathvision/diet/entity/UserLog.java new file mode 100644 index 0000000..a63bdb7 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/UserLog.java @@ -0,0 +1,41 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.domian.ClientType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "user_log") +public class UserLog { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uid", nullable = false, length = 18) + private String uid; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumsUsingName }) + @Enumerated(EnumType.STRING) + @Column(name = "client_type", columnDefinition="ENUM('web','android','ios')", nullable = false) + private ClientType clientType; + + @Column(name = "client_version", nullable = false) + private String clientVersion; + + @Column(name = "login", nullable = false) + private Instant login; + + @Column(name = "logout") + private Instant logout; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/UserMessage.java b/diet-dao/src/main/java/com/mathvision/diet/entity/UserMessage.java new file mode 100644 index 0000000..9c41304 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/UserMessage.java @@ -0,0 +1,47 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.domian.MessageType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "user_message") +public class UserMessage { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uid", nullable = false, length = 45) + private String uid; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumsUsingName }) + @Enumerated(EnumType.STRING) + @Column(name = "type", columnDefinition="ENUM('code','notify','message')", nullable = false) + private MessageType type; + + @Column(name = "content", nullable = false, length = 128) + private String content; + + @Column(name = "status", nullable = false) + private Boolean status = false; + + @Column(name = "operate", length = 18) + private String operate; + + @Column(name = "created") + private Instant created; + + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/UserRole.java b/diet-dao/src/main/java/com/mathvision/diet/entity/UserRole.java new file mode 100644 index 0000000..e9760e9 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/UserRole.java @@ -0,0 +1,44 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "user_role") +public class UserRole { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uid", nullable = false, length = 18) + private String uid; + + @Column(name = "role_id", columnDefinition = "int UNSIGNED not null") + private Long roleId; + + @Column(name = "vender", nullable = false) + private Long vender; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/UserSession.java b/diet-dao/src/main/java/com/mathvision/diet/entity/UserSession.java new file mode 100644 index 0000000..3d5c593 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/UserSession.java @@ -0,0 +1,50 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.domian.ClientType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "user_session") +public class UserSession { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uid", nullable = false, length = 18) + private String uid; + + @Column(name = "vender", nullable = false, length = 64) + private String vender; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumsUsingName }) + @Enumerated(EnumType.STRING) + @Column(name = "client_type", columnDefinition="ENUM('web','android','ios')", nullable = false) + private ClientType clientType; + + @Column(name = "client_version", nullable = false, length = 45) + private String clientVersion; + + @Column(name = "expired_time", nullable = false) + private Long expiredTime; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/Vender.java b/diet-dao/src/main/java/com/mathvision/diet/entity/Vender.java new file mode 100644 index 0000000..e257b28 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/Vender.java @@ -0,0 +1,90 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.annotation.JSONField; +import com.mathvision.diet.convert.VenderTypeConvert; +import com.mathvision.diet.domian.VenderType; +import lombok.*; + +import javax.persistence.*; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "vender") +public class Vender { + + public Vender (Long id, String name, String account, VenderType category) { + setId(id); + setAccount(account); + setName(name); + setCategory(category); + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "account", length = 16, nullable = false) + private String account; + + @Column(name = "name", nullable = false, length = 64) + private String name; + + @JSONField(serializeFeatures = { JSONWriter.Feature.WriteEnumUsingToString }) + @Convert(converter = VenderTypeConvert.class) + @Column(name = "category", columnDefinition="ENUM('学校','医院','事业单位','其他')") + private VenderType category; + + @Column(name = "status", nullable = false) + private Boolean status = false; + + @Lob + @Column(name = "icon") + private String icon; + + @Column(name = "url", length = 64) + private String url; + + @Column(name = "province", length = 20) + private String province; + + @Column(name = "city", length = 20) + private String city; + + @Column(name = "area", length = 20) + private String area; + + @Column(name = "address", length = 64) + private String address; + + @Column(name = "phone", length = 16) + private String phone; + + @Column(name = "contacts", length = 16) + private String contacts; + + @Column(name = "email", length = 32) + private String email; + + @Column(name = "expire") + private Instant expire; + + @JSONField(serialize = false) + @Column(name = "operate", length = 18) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/entity/VenderConfig.java b/diet-dao/src/main/java/com/mathvision/diet/entity/VenderConfig.java new file mode 100644 index 0000000..5c3638c --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/entity/VenderConfig.java @@ -0,0 +1,51 @@ +package com.mathvision.diet.entity; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.*; + +import javax.persistence.*; +import java.math.BigDecimal; +import java.time.Instant; + +@Builder +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Entity +@Table(name = "vender_config") +public class VenderConfig { + @JSONField(serialize = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "vender", nullable = false) + private Long vender; + + @JSONField(format = "#0.00") + @Column(name = "breakfast", precision = 5, scale = 2) + private BigDecimal breakfast; + + @JSONField(format = "#0.00") + @Column(name = "lunch", precision = 5, scale = 2) + private BigDecimal lunch; + + @JSONField(format = "#0.00") + @Column(name = "dinner", precision = 5, scale = 2) + private BigDecimal dinner; + + @JSONField(serialize = false) + @Column(name = "operate", length = 45) + private String operate; + + @JSONField(serialize = false) + @Column(name = "created") + private Instant created; + + @JSONField(serialize = false) + @Column(name = "modify") + private Instant modify; + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/DishRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/DishRepository.java new file mode 100644 index 0000000..7731fc9 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/DishRepository.java @@ -0,0 +1,26 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.Dish; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; + +import java.util.Collection; +import java.util.List; + +public interface DishRepository extends JpaRepository, JpaSpecificationExecutor { + long deleteByVender(Long vender); + @Query("select new Dish(d.id, d.name, d.marks, d.ingredient) from Dish d where d.name like ?1 order by d.id DESC") + List findByNameLikeOrderByIdDesc(String name); + boolean existsByVenderAndNameAndIdNot(Long vender, String name, Long id); + boolean existsByVenderAndIdIn(Long vender, Collection ids); + long deleteByIdInAndVender(Collection ids, Long vender); + @Query("select d from Dish d where d.vender = ?1 and d.id in ?2") + List findByVenderAndIdIn(Long vender, Collection ids); + @Query("select d from Dish d where d.id in ?1") + List findByIdIn(Collection ids); + @Query("select d from Dish d where d.vender = ?1") + List findByVender(Long vender); + Dish findByIdAndVender(Long id, Long vender); + boolean existsByVenderAndName(Long vender, String name); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/FoodCategoryRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodCategoryRepository.java new file mode 100644 index 0000000..e90953b --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodCategoryRepository.java @@ -0,0 +1,8 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.FoodCategory; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface FoodCategoryRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/FoodMarkRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodMarkRepository.java new file mode 100644 index 0000000..d36df18 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodMarkRepository.java @@ -0,0 +1,8 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.FoodMark; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface FoodMarkRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/FoodNutrientRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodNutrientRepository.java new file mode 100644 index 0000000..d82fcb9 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/FoodNutrientRepository.java @@ -0,0 +1,8 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.FoodNutrient; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface FoodNutrientRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientDTORepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientDTORepository.java new file mode 100644 index 0000000..5212249 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientDTORepository.java @@ -0,0 +1,16 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.domian.IngredientDTO; +import com.mathvision.diet.entity.IngredientMark; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; + +public interface IngredientDTORepository extends JpaRepository , JpaSpecificationExecutor { + @Query(countQuery = "SELECT count(1) FROM ingredient a left join (select ingredient as `key`, mark from ingredient_mark where vender = ?1) b on a.key = b.key where 1=1 and if(IFNULL(?2,'') ='', 1=1, a.type = ?2) and if(IFNULL(?3,'') ='', 1=1, b.mark = ?3) and if(IFNULL(?4,'') ='', 1=1, (a.key like concat(?4, '%') or a.name like concat(?4, '%') ))", + value = "SELECT a.key, a.name, a.type, a.nutrient, b.mark, a.modify FROM ingredient a left join (select ingredient as `key`, mark from ingredient_mark where vender = ?1) b on a.key = b.key where 1=1 and if(IFNULL(?2,'') ='', 1=1, a.type = ?2) and if(IFNULL(?3,'') ='', 1=1, b.mark = ?3) and if(IFNULL(?4,'') ='', 1=1, (a.key like concat(?4, '%') or a.name like concat(?4, '%') )) order by a.id DESC", nativeQuery = true) + Page findByVenderAndTypeAndMarkAndKey(Long vender, String type, String mark, String key, Pageable pageable); + +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientMarkRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientMarkRepository.java new file mode 100644 index 0000000..473ff9c --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientMarkRepository.java @@ -0,0 +1,20 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.domian.MarkType; +import com.mathvision.diet.entity.IngredientMark; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +public interface IngredientMarkRepository extends JpaRepository, JpaSpecificationExecutor { + long deleteByIngredient(String ingredient); + long deleteByVender(Long vender); + @Transactional + @Modifying + @Query("update IngredientMark i set i.mark = ?1, i.operate = ?2 where i.vender = ?3 and i.ingredient = ?4") + int updateMarkAndOperateByVenderAndIngredient(MarkType mark, String operate, Long vender, String ingredient); + @Transactional + long deleteByVenderAndIngredient(Long vender, String ingredient); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientRepository.java new file mode 100644 index 0000000..a27d31c --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/IngredientRepository.java @@ -0,0 +1,42 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.Ingredient; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +public interface IngredientRepository extends JpaRepository, JpaSpecificationExecutor { + @Query("select new Ingredient(i.key,i.name,i.type) from Ingredient i where i.key like concat(?1, '%') or i.name like concat(?1, '%') order by i.key") + List findByKeyStartsWithOrNameStartsWithOrderByKeyAsc(String keyword); + @Query("select new Ingredient(i.key,i.name,i.type) from Ingredient i where i.key in ?1 order by i.key") + List findByKeyInOrderByKeyAsc(Collection keys); + @Query("select new Ingredient(i.key,i.name) from Ingredient i") + List findKeyNameMap(); + + @Query("select i from Ingredient i where i.key in ?1") + List findByKeyIn(Collection keys); + boolean existsByKey(String key); + @Transactional + long deleteByKey(String key); + Ingredient findByKey(String key); + + Page findByTypeOrderByIdDesc(String type, Pageable pageable); + + Page findByKeyStartsWithOrderByIdDesc(String key, Pageable pageable); + + Page findByNameStartsWithOrderByIdDesc(String name, Pageable pageable); + + Page findByTypeAndKeyStartsWithOrderByIdDesc(String type, String key, Pageable pageable); + + Page findByTypeAndNameStartsWithOrderByIdDesc(String type, String name, Pageable pageable); + + @Query("select (count(i) > 0) from Ingredient i where i.id <> ?1 and (i.key = ?2 or i.name = ?3)") + boolean existsByIdNotAndKeyOrName(Long id, String key, String name); + boolean existsByKeyOrName(String key, String name); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/MenuApproveRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuApproveRepository.java new file mode 100644 index 0000000..d3f77a7 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuApproveRepository.java @@ -0,0 +1,15 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.MenuApprove; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +public interface MenuApproveRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + @Modifying + @Query("delete from MenuApprove m where DATEDIFF(NOW(), m.modify) > 30") + void scanExpired(); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/MenuDishRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuDishRepository.java new file mode 100644 index 0000000..d01824e --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuDishRepository.java @@ -0,0 +1,20 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.MenuDish; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +import java.util.Collection; +import java.util.List; + +public interface MenuDishRepository extends JpaRepository, JpaSpecificationExecutor { + long deleteByVender(Long vender); + MenuDish findByMenuAndVenderAndDayAndDishAndMeal(Long menu, Long vender, Long day, Long dish, String meal); + List findByMenuAndVenderAndDay(Long menu, Long vender, Long day); + List findByMenuAndVender(Long menu, Long vender); + MenuDish findByIdAndVender(Long id, Long vender); + List findByMenu(Long menu); + long deleteByMenuAndDayGreaterThanAndMealNotIn(Long menu, Long day, Collection meals); + long deleteByMenuAndVender(Long menu, Long vender); + long deleteByMenu(Long menu); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/MenuRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuRepository.java new file mode 100644 index 0000000..45819f6 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/MenuRepository.java @@ -0,0 +1,53 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.domian.MenuCountDTO; +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface MenuRepository extends JpaRepository, JpaSpecificationExecutor { + List findByNutrient(Long nutrient); + long deleteByVender(Long vender); + @Query(value = "select m.id from menu m " + + "where m.vender = ?1 and m.status = ?2 and (WEEKDAY(NOW()) + 1) <= m.day and now() between m.start_time and m.end_time " + + "order by m.id DESC " + + "limit 1", nativeQuery = true) + Long findCurrentMenu(Long vender, int status); + @Query("select new com.mathvision.diet.domian.MenuCountDTO(m.status, count(m)) from Menu m where m.status in ?1 group by m.status") + List count(Collection statuses); + @Query("select count(m) from Menu m where m.vender = ?1") + long countByVender(Long vender); + @Transactional + @Modifying + @Query("update Menu m set m.status = ?1, m.scale = ?2, m.startTime = ?3, m.endTime = ?4 where m.id = ?5") + int updateStatusAndScaleAndStartTimeAndEndTimeById(MenuStatus status, Map scale, Instant startTime, Instant endTime, Long id); + @Transactional + @Modifying + @Query("update Menu m set m.status = ?1, m.approve = ?2, m.operate = ?3 where m.id = ?4") + int updateStatusAndApproveAndOperateById(MenuStatus status, String approve, String operate, Long id); + @Transactional + @Modifying + @Query("update Menu m set m.status = ?1, m.operate = ?2 where m.id = ?3") + int updateStatusAndOperateById(MenuStatus status, String operate, Long id); + @Transactional + @Modifying + @Query("update Menu m set m.status = ?1, m.startTime = ?2, m.endTime = ?3, m.operate = ?4 where m.id = ?5") + int updateStatusAndStartTimeAndEndTimeAndOperateById(MenuStatus status, Instant startTime, Instant endTime, String operate, Long id); + long deleteByIdAndVender(Long id, Long vender); + + Menu findByIdAndVender(Long id, Long vender); + + @Transactional + @Modifying + @Query(value = "update menu v set v.status = 2 where v.status = 5 and v.end_time < now()", nativeQuery = true) + void scanExpired(); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/NutritionRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/NutritionRepository.java new file mode 100644 index 0000000..b2b40fb --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/NutritionRepository.java @@ -0,0 +1,13 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.Nutrition; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface NutritionRepository extends JpaRepository, JpaSpecificationExecutor { + boolean existsByName(String name); + Page findByNameStartsWithOrderByIdDesc(String name, Pageable pageable); + Nutrition findByName(String name); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/RoleItemRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/RoleItemRepository.java new file mode 100644 index 0000000..42a3fff --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/RoleItemRepository.java @@ -0,0 +1,8 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.RoleItem; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface RoleItemRepository extends JpaRepository, JpaSpecificationExecutor { +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/RoleRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/RoleRepository.java new file mode 100644 index 0000000..c9a4e4d --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/RoleRepository.java @@ -0,0 +1,35 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.domian.RoleType; +import com.mathvision.diet.entity.Role; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public interface RoleRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + long deleteByVender(Long vender); + + boolean existsByIdAndVender(Long id, Long vender); + + @Transactional + @Modifying + @Query("update Role r set r.roleItems = ?1, r.operate = ?2 where r.id = ?3 and r.vender = ?4 and r.roleType != '系统'") + int updateRoleItemsAndOperateByIdAndVenderAndRoleTypeNot(String roleItems, String operate, Long id, Long vender); + + @Transactional + @Modifying + @Query("update Role r set r.roleName = ?1, r.operate = ?2 where r.id = ?3 and r.vender = ?4 and r.roleType != '系统'") + int updateRoleNameAndOperateByIdAndVenderAndRoleTypeNot(String roleName, String operate, Long id, Long vender); + + @Query("DELETE FROM Role d WHERE d.id = ?1 AND d.vender = ?2 AND ?3 != d.roleType") + long deleteByIdAndVenderAndRoleTypeNot(Long id, Long vender, RoleType roleType); + List findByVender(Long vender); + + @Query("SELECT d FROM Role d WHERE d.vender = ?1 AND ?2= d.roleType") + Role findByVenderForDefaultRole(Long vender, RoleType roleType); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/UserLogRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/UserLogRepository.java new file mode 100644 index 0000000..bffe209 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/UserLogRepository.java @@ -0,0 +1,23 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.UserLog; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.lang.NonNull; +import org.springframework.transaction.annotation.Transactional; + +import java.time.Instant; + +public interface UserLogRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + @Modifying + @Query(value = "update user_log u set u.logout = ?1 where u.uid = ?2 order by u.id desc limit 1", nativeQuery = true) + void updateLogoutByUid(@NonNull Instant logout, @NonNull String uid); + + @Transactional + @Modifying + @Query("delete from UserLog m where DATEDIFF(NOW(), m.login) > 30") + void scanExpired(); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/UserMessageRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/UserMessageRepository.java new file mode 100644 index 0000000..7bc75b3 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/UserMessageRepository.java @@ -0,0 +1,16 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.UserMessage; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +public interface UserMessageRepository extends JpaRepository, JpaSpecificationExecutor { + + @Transactional + @Modifying + @Query("delete from UserMessage m where DATEDIFF(NOW(), m.modify) > 30") + void scanExpired(); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/UserRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/UserRepository.java new file mode 100644 index 0000000..2c00e04 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/UserRepository.java @@ -0,0 +1,30 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +public interface UserRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + long deleteByUidIn(Collection uid); + List findByUidIn(Collection uid); + boolean existsByUidAndPwdAndStatus(String uid, String pwd, Boolean status); + @Transactional + @Modifying + @Query("update User u set u.pwd = ?1 where u.uid = ?2") + int updatePwdByUid(String pwd, String uid); + @Transactional + @Modifying + @Query("update User u set u.name = ?1 where u.uid = ?2") + int updateNameByUid(String name, String uid); + @Transactional + long deleteByUid(String uid); + boolean existsByUid(String uid); + User findByUidAndStatus(String uid, Boolean status); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/UserRoleRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/UserRoleRepository.java new file mode 100644 index 0000000..b36eea0 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/UserRoleRepository.java @@ -0,0 +1,26 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.UserRole; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public interface UserRoleRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + long deleteByVender(Long vender); + @Transactional + long deleteByUidAndVender(String uid, Long vender); + long countByRoleIdAndVenderAndUidNot(Long roleId, Long vender, String uid); + boolean existsByUidAndVender(String uid, Long vender); + @Transactional + @Modifying + @Query("update UserRole u set u.roleId = ?1, u.operate = ?2 where u.uid = ?3 and u.vender = ?4") + int updateRoleIdAndOperateByUidAndVender(Long roleId, String operate, String uid, Long vender); + boolean existsByRoleIdAndVender(Long roleId, Long vender); + List findByVender(Long vender); + UserRole findByUid(String uid); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/UserSessionRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/UserSessionRepository.java new file mode 100644 index 0000000..7476805 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/UserSessionRepository.java @@ -0,0 +1,16 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.UserSession; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +public interface UserSessionRepository extends JpaRepository, JpaSpecificationExecutor { + + @Transactional + @Modifying + @Query("delete from UserSession m where DATEDIFF(NOW(), m.modify) > 30") + void scanExpired(); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/VenderConfigRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/VenderConfigRepository.java new file mode 100644 index 0000000..b4f88d1 --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/VenderConfigRepository.java @@ -0,0 +1,19 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.entity.VenderConfig; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +public interface VenderConfigRepository extends JpaRepository, JpaSpecificationExecutor { + @Transactional + @Modifying + @Query("update VenderConfig v set v.breakfast = ?1, v.lunch = ?2, v.dinner = ?3, v.operate = ?4 where v.vender = ?5") + int updateBreakfastAndLunchAndDinnerAndOperateByVender(BigDecimal breakfast, BigDecimal lunch, BigDecimal dinner, String operate, Long vender); + long deleteByVender(Long vender); + VenderConfig findByVender(Long vender); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/repository/VenderRepository.java b/diet-dao/src/main/java/com/mathvision/diet/repository/VenderRepository.java new file mode 100644 index 0000000..3d8594b --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/repository/VenderRepository.java @@ -0,0 +1,33 @@ +package com.mathvision.diet.repository; + +import com.mathvision.diet.domian.VenderType; +import com.mathvision.diet.entity.Vender; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +public interface VenderRepository extends JpaRepository, JpaSpecificationExecutor { + Page findByCategoryAndNameLikeOrderByIdDesc(VenderType category, String name, Pageable pageable); + Page findByCategoryOrderByIdDesc(VenderType category, Pageable pageable); + @Query("select new Vender(v.id, v.name, v.account, v.category) from Vender v where v.id in ?1") + List findByIdIn(Collection ids); + @Query("select new Vender(v.id, v.name, v.account, v.category) from Vender v where v.status = ?1 and v.name like concat(?2, '%') order by v.name DESC") + List findByStatusAndNameLikeOrderByNameDesc(Boolean status, String name); + @Query("select new Vender(v.id, v.name, v.account, v.category) from Vender v where v.status = ?1 order by v.name") + List findByStatusOrderByNameAsc(Boolean status); + Page findByNameLikeOrderByIdDesc(String name, PageRequest pageRequest); + boolean existsByName(String name); + @Transactional + @Modifying + @Query("update Vender v set v.status = false where v.status = true and v.expire < now()") + void scanExpired(); + Vender findByIdAndStatus(Long vender, boolean status); +} \ No newline at end of file diff --git a/diet-dao/src/main/java/com/mathvision/diet/utils/GZIPUtils.java b/diet-dao/src/main/java/com/mathvision/diet/utils/GZIPUtils.java new file mode 100644 index 0000000..39ef65a --- /dev/null +++ b/diet-dao/src/main/java/com/mathvision/diet/utils/GZIPUtils.java @@ -0,0 +1,65 @@ +package com.mathvision.diet.utils; + +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; + +@Slf4j +public class GZIPUtils { + + public static final String GZIP_ENCODE_UTF_8 = "UTF-8"; + + public static String compress(String str) { + return compress(str, GZIP_ENCODE_UTF_8); + } + + public static String compress(String str, String encoding) { + if (str == null || str.length() == 0) { + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gzip; + try { + gzip = new GZIPOutputStream(out); + gzip.write(str.getBytes(encoding)); + gzip.close(); + } catch (Exception e) { + log.error("gzip compress error.", e); + } + return out.toString(); + } + + public static String decompress(String str) { + return decompressToString(str, GZIP_ENCODE_UTF_8); + } + + public static String decompressToString(String str, String encoding) { + if (str == null || str.length() == 0) { + return null; + } + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(str.getBytes(encoding)); + GZIPInputStream unGzip = new GZIPInputStream(in); + byte[] buffer = new byte[256]; + int n; + while ((n = unGzip.read(buffer)) >= 0) { + out.write(buffer, 0, n); + } + return out.toString(encoding); + } catch (Exception e) { + log.error("gzip decompressToString to string error.", e); + } + return null; + } + + public static void main(String[] args) { + String str ="data:image/jpeg;base64,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"; + System.out.println("原数据:" + str); + System.out.println("压缩后数据:" + (str)); + System.out.println("解压缩后字符串:" + decompress(compress(str))); + } +} diff --git a/diet-web/pom.xml b/diet-web/pom.xml new file mode 100644 index 0000000..576e53e --- /dev/null +++ b/diet-web/pom.xml @@ -0,0 +1,65 @@ + + + 4.0.0 + + + com.mathvision.diet + diet + 1.0-SNAPSHOT + + diet-web + jar + + + 8 + 8 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-json + + + + + com.auth0 + java-jwt + 4.3.0 + + + com.mathvision.diet + diet-core + 1.0-SNAPSHOT + + + + + + + src/main/resources + + *.properties + *.xml + *.yml + **/*.so + **/*.dll + static/** + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/Application.java b/diet-web/src/main/java/com/mathvision/diet/Application.java new file mode 100644 index 0000000..01206ef --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/Application.java @@ -0,0 +1,13 @@ +package com.mathvision.diet; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@EnableScheduling +@SpringBootApplication +public class Application { + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalExceptionHandler.java b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalExceptionHandler.java new file mode 100644 index 0000000..6ed6523 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalExceptionHandler.java @@ -0,0 +1,79 @@ +package com.mathvision.diet.aspect; + +import com.alibaba.fastjson2.JSON; +import com.mathvision.diet.domain.Result; +import com.mathvision.diet.exception.DietException; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.MissingRequestCookieException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import org.springframework.web.multipart.MultipartException; + +import javax.servlet.http.HttpServletRequest; +import java.nio.charset.StandardCharsets; + +@Slf4j +@Controller +@ControllerAdvice +public class GlobalExceptionHandler { + + @ResponseBody + @ExceptionHandler(DietException.class) + public Result errorHandler(DietException e) { + return _innerHandler(e.getResult(), e, false); + } + + @ResponseBody + @ExceptionHandler({IllegalArgumentException.class}) + public Result errorHandler(IllegalArgumentException e) { + return _innerHandler(Result.ILLEGAL_ARGUMENT, e, false); + } + + @ResponseBody + @ExceptionHandler({MissingServletRequestParameterException.class, MissingPathVariableException.class, MissingRequestCookieException.class, MissingRequestHeaderException.class, MissingServletRequestParameterException.class}) + public Result errorHandler(MissingServletRequestParameterException e) { + return _innerHandler(Result.ILLEGAL_ARGUMENT, e, false); + } + + @ResponseBody + @ExceptionHandler({MultipartException.class, MethodArgumentTypeMismatchException.class}) + public Result errorHandler(MultipartException e) { + return _innerHandler(Result.NOT_SUPPORT, e, false); + } + + @ResponseBody + @ExceptionHandler + public Result errorHandler(Exception e) { + return _innerHandler(Result.ERROR, e, true); + } + + @SneakyThrows + private Result _innerHandler(Result result, Exception e, boolean needExceptionTrace) { + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + if (requestAttributes != null) { + if (requestAttributes.getResponse() != null) { + byte[] response = JSON.toJSONString(new Result(result.getCode(), result.equals(Result.ERROR) ? "系统异常!" : e.getMessage())).getBytes(StandardCharsets.UTF_8); + requestAttributes.getResponse().setContentType("text/json; charset=utf-8"); + requestAttributes.getResponse().setStatus(result.getCode()); + requestAttributes.getResponse().setContentLength(response.length); + requestAttributes.getResponse().getOutputStream().write(response); + } + HttpServletRequest request = requestAttributes.getRequest(); + if (!needExceptionTrace) { + log.error(String.format("[GlobalExceptionHandler:%s] %s %s exception=%s", request.getMethod(), request.getServletPath(), JSON.toJSONString(request.getParameterMap()), e.getMessage())); + } else { + log.error(String.format("[GlobalExceptionHandler:%s] %s %s exception=%s", request.getMethod(), request.getServletPath(), JSON.toJSONString(request.getParameterMap()), e.getMessage()), e); + } + } + return result; + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalRequestAspect.java b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalRequestAspect.java new file mode 100644 index 0000000..098f9f6 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalRequestAspect.java @@ -0,0 +1,87 @@ +package com.mathvision.diet.aspect; + +import com.alibaba.fastjson2.JSON; +import com.mathvision.diet.constant.Constant; +import com.mathvision.diet.domain.Result; +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.domian.AuthType; +import com.mathvision.diet.entity.RoleItem; +import com.mathvision.diet.exception.DietException; +import com.mathvision.diet.service.EnumService; +import com.mathvision.diet.utils.JWTUtils; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@Configuration +@ControllerAdvice +public class GlobalRequestAspect implements HandlerInterceptor, WebMvcConfigurer { + + @Resource + EnumService enumService; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(this) + .addPathPatterns("/api/**") + .excludePathPatterns("/api/login") + .excludePathPatterns("/api/logout"); + } + + @Override + public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws IOException { + if (handler instanceof ResourceHttpRequestHandler) { + response.setStatus(Result.NOT_SUPPORT.getCode()); + response.setCharacterEncoding("UTF-8"); + response.setContentType("text/json; charset=utf-8"); + response.getOutputStream().write(JSON.toJSONString(Result.NOT_SUPPORT).getBytes(StandardCharsets.UTF_8)); + return false; + } + return verifySession(request, response); + } + + private boolean verifySession(HttpServletRequest request, HttpServletResponse response) { + UserDO userDO = (UserDO)request.getSession().getAttribute(Constant.SESSION_USER_KEY); + if (userDO == null) { + String token = request.getHeader(Constant.TOKEN_HEADER_KEY); + if (StringUtils.isBlank(token)) { + throw new DietException(Result.NOT_LOGIN); + } + userDO = JWTUtils.verify(token); + if (userDO == null) { + throw new DietException(Result.NOT_LOGIN); + } else { + userDO.setRoleItems(enumService.getRoleItems(userDO.getRoleItems().stream().map(RoleItem::getId).collect(Collectors.toList()))); + request.getSession().setMaxInactiveInterval(Constant.TOKEN_EXPIRE_SECOND); + request.getSession().setAttribute(Constant.SESSION_USER_KEY, userDO); + request.getSession().setAttribute(Constant.SESSION_USER_TYPE, userDO.isAdmin()); + } + } + if (!hasRole(userDO.isAdmin() ? AuthType.SERVER : AuthType.CLIENT, userDO.getRoleItems(), request.getRequestURI().toLowerCase(), request.getMethod().toLowerCase())) { + throw new DietException(Result.NOT_PRIVILEGED); + } + response.setHeader(Constant.TOKEN_HEADER_KEY, JWTUtils.getToken(userDO)); + return true; + } + + private boolean hasRole(AuthType authType, List roleItems, String url, String method) { + return !enumService.getAuthItems(authType).contains(url) || roleItems.parallelStream() + .map(item -> item.getItemValue().split(":", 2)) + .anyMatch(item -> item.length == 2 && item[0].contains(method) && url.matches("^/api/(" + item[1] + ")$")); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalResponseAspect.java b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalResponseAspect.java new file mode 100644 index 0000000..09c66a6 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/aspect/GlobalResponseAspect.java @@ -0,0 +1,53 @@ +package com.mathvision.diet.aspect; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.config.FastJsonConfig; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; +import com.mathvision.diet.constant.Constant; +import com.mathvision.diet.domain.Result; +import com.mathvision.diet.domain.ResultCode; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; + +import java.nio.charset.StandardCharsets; + +@Slf4j +@Configuration +@ControllerAdvice +public class GlobalResponseAspect implements ResponseBodyAdvice { + + @Override + public boolean supports(@NonNull MethodParameter methodParameter, @NonNull Class clazz) { + return true; + } + + @Bean + FastJsonHttpMessageConverter fastJsonHttpMessageConverters() { + FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); + FastJsonConfig jsonConfig = new FastJsonConfig(); + jsonConfig.setCharset(StandardCharsets.UTF_8); + jsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); + jsonConfig.setSerializerFeatures(SerializerFeature.BrowserCompatible); + converter.setFastJsonConfig(jsonConfig); + converter.setDefaultCharset(StandardCharsets.UTF_8); + converter.setSupportedMediaTypes(Constant.SUPPORTED_MEDIA_TYPES); + return converter; + } + + @Override + public Object beforeBodyWrite(Object result, @NonNull MethodParameter methodParameter, @NonNull MediaType mediaType, @NonNull Class> clazz, @NonNull ServerHttpRequest serverHttpRequest, @NonNull ServerHttpResponse serverHttpResponse) { + if (result instanceof Result) { + return result; + } + return new Result(ResultCode.success, result); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/constant/Constant.java b/diet-web/src/main/java/com/mathvision/diet/constant/Constant.java new file mode 100644 index 0000000..a1a3eb3 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/constant/Constant.java @@ -0,0 +1,32 @@ +package com.mathvision.diet.constant; + +import com.google.common.collect.Lists; +import org.springframework.http.MediaType; + +import java.util.List; + +public class Constant { + public static final int TOKEN_EXPIRE_SECOND = 7200; + public static final String TOKEN_HEADER_KEY = "Authorization"; + public static final String SESSION_USER_KEY = "user"; + public static final String SESSION_USER_TYPE = "type"; + + public static final List SUPPORTED_MEDIA_TYPES = Lists.newArrayList( + MediaType.APPLICATION_JSON, + MediaType.APPLICATION_ATOM_XML, + MediaType.APPLICATION_FORM_URLENCODED, + MediaType.APPLICATION_OCTET_STREAM, + MediaType.APPLICATION_PDF, + MediaType.APPLICATION_RSS_XML, + MediaType.APPLICATION_XHTML_XML, + MediaType.APPLICATION_XML, + MediaType.IMAGE_GIF, + MediaType.IMAGE_JPEG, + MediaType.IMAGE_PNG, + MediaType.TEXT_EVENT_STREAM, + MediaType.TEXT_HTML, + MediaType.TEXT_MARKDOWN, + MediaType.TEXT_PLAIN, + MediaType.TEXT_XML + ); +} diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/AuthController.java b/diet-web/src/main/java/com/mathvision/diet/controller/AuthController.java new file mode 100644 index 0000000..67e4189 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/AuthController.java @@ -0,0 +1,58 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domain.Result; +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.domian.ClientType; +import com.mathvision.diet.entity.Vender; +import com.mathvision.diet.exception.DietException; +import com.mathvision.diet.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.time.Instant; + +@RequestMapping("/api") +@Controller +public class AuthController extends BaseController { + @Resource + private UserService userService; + + @ResponseBody + @RequestMapping("login") + public UserDO login(@RequestParam String uid, @RequestParam String pwd, @RequestParam(required = false, defaultValue = "WEB") String clientType, @RequestParam(required = false, defaultValue = "1.0") String clientVersion) { + if(StringUtils.isBlank(uid) || StringUtils.isBlank(pwd)) { + throw new DietException(Result.ILLEGAL_ARGUMENT); + } + UserDO userDO = userService.login(uid, pwd, ClientType.valueOf(clientType), clientVersion); + if (userDO == null) { + throw new DietException(Result.INVALID_USER_PASS); + } + Vender vender = userDO.getVender(); + if (vender != null && (!vender.getStatus() || Instant.now().isAfter(vender.getExpire()))) { + throw new DietException(Result.EXPIRED); + } + setSession(userDO); + return userDO; + } + + @ResponseBody + @RequestMapping(value = "password", method = RequestMethod.POST) + public void modUser(@RequestParam String oldPassword, @RequestParam String password) { + Assert.isTrue(userService.checkUser(getUid(), oldPassword), "[参数错误]用户名密码错误!"); + userService.changeUser(getUid(), null, password, null, getVender(), getUid()); + delSession(); + } + + @ResponseBody + @RequestMapping("logout") + public void logout() { + userService.logout(getUid()); + delSession(); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/BaseController.java b/diet-web/src/main/java/com/mathvision/diet/controller/BaseController.java new file mode 100644 index 0000000..f750d31 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/BaseController.java @@ -0,0 +1,97 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.constant.Constant; +import com.mathvision.diet.domain.Result; +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.exception.DietException; +import com.mathvision.diet.utils.JWTUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.util.Date; +import java.util.Objects; + +@Slf4j +public class BaseController { + + public HttpServletRequest getRequest() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + } + + public HttpServletResponse getResponse() { + return ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getResponse(); + } + + public UserDO getSession() { + return (UserDO)getRequest().getSession().getAttribute(Constant.SESSION_USER_KEY); + } + + public void setSession(UserDO userDO) { + HttpSession httpSession = getRequest().getSession(); + if (httpSession == null) { + return; + } + httpSession.setMaxInactiveInterval(Constant.TOKEN_EXPIRE_SECOND); + httpSession.setAttribute(Constant.SESSION_USER_KEY, userDO); + httpSession.setAttribute(Constant.SESSION_USER_TYPE, userDO.isAdmin()); + + getResponse().setHeader(Constant.TOKEN_HEADER_KEY, JWTUtils.getToken(userDO)); + } + + public void delSession() { + HttpSession httpSession = getRequest().getSession(); + if (httpSession == null) { + return; + } + httpSession.removeAttribute(Constant.SESSION_USER_KEY); + httpSession.removeAttribute(Constant.SESSION_USER_TYPE); + getResponse().setHeader(Constant.TOKEN_HEADER_KEY, null); + } + + public String getUid() { + UserDO userDO = getSession(); + return userDO == null ? null : userDO.getUid(); + } + + public Long getRoleId() { + UserDO userDO = getSession(); + return userDO == null ? null : userDO.getRoleId(); + } + + public Long getVender() { + return isAdmin() ? 0 : getSession().getVender().getId(); + } + + public boolean isAdmin() { + return (Boolean) getRequest().getSession().getAttribute(Constant.SESSION_USER_TYPE); + } + + public Long auth(Long venderId) { + if (venderId == null) { + venderId = getVender(); + } + if (!isAdmin() && !venderId.equals(getVender())) { + throw new DietException(Result.NOT_PRIVILEGED); + } + return venderId; + } + + public Date parseDate(String dateTime) { + return parseDate(dateTime, null); + } + + public Date parseDate(String dateTime, Date defaultDate) { + try { + return StringUtils.isBlank(dateTime) ? defaultDate : DateUtils.parseDate(dateTime, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss"); + } catch (Exception e) { + log.error("[BaseController] parseDate exception for input:" + dateTime); + return null; + } + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/BasicController.java b/diet-web/src/main/java/com/mathvision/diet/controller/BasicController.java new file mode 100644 index 0000000..20472d0 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/BasicController.java @@ -0,0 +1,24 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.service.EnumService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Map; + +@RequestMapping("/api") +@Controller +public class BasicController extends BaseController { + + @Resource + private EnumService enumService; + + @ResponseBody + @RequestMapping("enum") + public Map> all() { + return enumService.getAll(); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/DishController.java b/diet-web/src/main/java/com/mathvision/diet/controller/DishController.java new file mode 100644 index 0000000..7bc445a --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/DishController.java @@ -0,0 +1,136 @@ +package com.mathvision.diet.controller; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.google.common.collect.Lists; +import com.google.common.collect.Range; +import com.mathvision.diet.domain.DishLabelDO; +import com.mathvision.diet.domian.DishItemDTO; +import com.mathvision.diet.entity.Dish; +import com.mathvision.diet.entity.Ingredient; +import com.mathvision.diet.service.DishService; +import com.mathvision.diet.service.EnumService; +import com.mathvision.diet.service.IngredientService; +import com.mathvision.diet.service.VenderService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.time.Instant; +import java.util.List; +import java.util.stream.Collectors; + +@RequestMapping("/api/dish") +@Controller +public class DishController extends BaseController { + + @Resource + EnumService enumService; + + @Resource + DishService dishService; + + @Resource + VenderService venderService; + + @Resource + private IngredientService ingredientService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void add(@RequestParam String name, @RequestParam(required = false) List vendors, @RequestParam(required = false) String icon, @RequestParam(required = false) List month, @RequestParam String mark, @RequestParam String ingredient) { + Assert.isTrue(StringUtils.isNotBlank(name), "[参数错误]菜品名称必填!"); + Assert.isTrue(StringUtils.isNotBlank(mark), "[参数错误]菜品标签必填!"); + Assert.isTrue(enumService.checkMark(mark), "[参数错误]菜品标签不在取值范围内!"); + Assert.isTrue(CollectionUtils.isNotEmpty(month) && month.stream().allMatch(x -> Range.closed(1, 12).contains(x)), "[参数错误]请选择正确的月份!"); + Assert.isTrue(JSON.isValid(ingredient), "[参数错误]食材列表必填!"); + + List items = JSON.parseObject(ingredient, new TypeReference>(){}); + Assert.isTrue(items.stream().allMatch(x -> ingredientService.existsIngredientByKey(x.getKey())), "[参数错误]请选择系统存在的食材!"); + if (isAdmin()) { + Assert.isTrue(CollectionUtils.isNotEmpty(vendors), "[参数错误]单位列表必填!"); + } else { + vendors = Lists.newArrayList(getVender()); + } + Assert.isTrue(vendors.stream().allMatch(venderService::exists), "[[参数错误]请选择存在的单位!]"); + + Instant dateTime = Instant.now(); + dishService.add(vendors.stream().map(vender -> Dish.builder().name(name).vender(vender).marks(mark).month(month).icon(icon).ingredient(items).operate(getUid()).created(dateTime).modify(dateTime).build()).collect(Collectors.toList()), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delete(@RequestParam List ids) { + Assert.isTrue(CollectionUtils.isNotEmpty(ids), "[参数错误]菜品编号必填!"); + Assert.isTrue(isAdmin() || dishService.exists(getVender(), ids), "[参数错误]菜品不存在!"); + dishService.delete(ids, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void update(@RequestParam Long id, @RequestParam(required = false) String name, @RequestParam(required = false) String icon, @RequestParam(required = false) List month, @RequestParam(required = false) String mark, @RequestParam(required = false) String ingredient) { + Dish dish = isAdmin()? dishService.get(id) : dishService.get(id, getVender()); + Assert.isTrue(dish != null, "[参数错误]菜品不存在!"); + + boolean flag = false; + if(StringUtils.isNotBlank(name) && !StringUtils.equals(name, dish.getName())) { + Assert.isTrue(!dishService.exists(id, name, dish.getVender()), "[参数错误]菜品名称已存在!"); + dish.setName(name); + flag = true; + } + if(StringUtils.isNotBlank(icon) && !icon.equals(dish.getIcon())) { + dish.setIcon(icon); + flag = true; + } + if(StringUtils.isNotBlank(mark) && !mark.equals(dish.getMarks())) { + Assert.isTrue(enumService.checkMark(mark), "[参数错误]菜品标签不在取值范围内!"); + dish.setMarks(mark); + flag = true; + } + if(CollectionUtils.isNotEmpty(month)) { + Assert.isTrue(CollectionUtils.isNotEmpty(month) && month.stream().allMatch(x -> Range.closed(1, 12).contains(x)), "[参数错误]请选择正确的月份!"); + dish.setMonth(month); + flag = true; + } + if (JSON.isValid(ingredient)) { + List items = JSON.parseObject(ingredient, new TypeReference>(){}); + if(CollectionUtils.isNotEmpty(items)) { + Assert.isTrue(items.stream().allMatch(x -> ingredientService.existsIngredientByKey(x.getKey())), "[参数错误]请选择系统存在的食材!"); + dish.setIngredient(items); + flag = true; + } + } + if (flag) { + dishService.update(dish, getUid()); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Object query(@RequestParam(required = false) String keyword, @RequestParam(required = false) Long id, @RequestParam(required = false) String mark, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + if (id != null) { + return isAdmin() ? dishService.get(id) : dishService.get(id, getVender()); + } + return dishService.list(getVender(), keyword, mark, PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } + + @ResponseBody + @RequestMapping(value = "select", method = RequestMethod.GET) + public Object query(@RequestParam(required = false) String keyword) { + return dishService.query(keyword); + } + + @ResponseBody + @RequestMapping(value = "label", method = RequestMethod.GET) + public List label(@RequestParam(required = false) List ids) { + return dishService.label(ids, getVender()); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/IngredientController.java b/diet-web/src/main/java/com/mathvision/diet/controller/IngredientController.java new file mode 100644 index 0000000..403cb0d --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/IngredientController.java @@ -0,0 +1,137 @@ +package com.mathvision.diet.controller; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.mathvision.diet.domian.MarkType; +import com.mathvision.diet.entity.Ingredient; +import com.mathvision.diet.service.IngredientService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RequestMapping("/api/ingredient") +@Controller +public class IngredientController extends BaseController { + @Resource + IngredientService ingredientService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void addIngredient(@RequestParam String key, @RequestParam String name, @RequestParam String type, @RequestParam String nutrient) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.isTrue(JSON.isValid(nutrient), "[参数错误]营养素列表JSON解析错误!"); + Assert.isTrue(!ingredientService.existsIngredientByKey(key), "[参数错误]食材编号重复!"); + + ingredientService.addIngredient(Ingredient.builder().key(key).name(name).type(type).nutrient(JSON.parseObject(nutrient, new TypeReference>(){})).build(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delIngredient(@RequestParam List keys) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + keys = keys.stream().filter(key -> ingredientService.existsIngredientByKey(key)).collect(Collectors.toList()); + Assert.isTrue(CollectionUtils.isNotEmpty(keys), "[参数错误]请提供食材编号!"); + + ingredientService.delIngredient(keys, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void modIngredient(@RequestParam String key, @RequestParam(required = false) String name, @RequestParam(required = false) String type, @RequestParam(required = false) String nutrient) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.isTrue(nutrient == null || JSON.isValid(nutrient), "[参数错误]营养素列表JSON解析错误!"); + Ingredient ingredient = ingredientService.queryIngredientByKey(key); + Assert.isTrue(ingredient != null, "[参数错误]食材不存在!"); + + boolean flag = false; + if(StringUtils.isNotBlank(type) && !StringUtils.equals(type, ingredient.getType())) { + ingredient.setType(type); + flag = true; + } + if(StringUtils.isNotBlank(name) && !StringUtils.equals(name, ingredient.getName())) { + ingredient.setName(name); + flag = true; + } + if (JSON.isValid(nutrient)) { + ingredient.setNutrient(JSON.parseObject(nutrient, new TypeReference>(){})); + flag = true; + } + if (flag) { + ingredientService.modIngredient(ingredient, getUid()); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Page queryIngredient(@RequestParam(required = false) String keyword, @RequestParam(required = false) String type, @RequestParam(required = false) String mark, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + return ingredientService.queryIngredientByMark(getVender(), mark, keyword, type, PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } + + @ResponseBody + @RequestMapping(value = "select", method = RequestMethod.GET) + public List queryIngredient(@RequestParam(required = false) List keys, @RequestParam(required = false) String keyword) { + return CollectionUtils.isNotEmpty(keys) ? ingredientService.getByKeys(keys) : ingredientService.getByKeyword(keyword); + } + + @ResponseBody + @RequestMapping(value = "mark", method = RequestMethod.PUT) + public void addMark(@RequestParam String mark, @RequestParam String key) { + MarkType markType = MarkType.toType(mark); + Assert.isTrue(!isAdmin(), "[参数错误]无操作权限!"); + Assert.notNull(markType, "[参数错误]标记取值:常用/忌用!"); + Assert.isTrue(ingredientService.existsIngredientByKey(key), "[参数错误]食材不存在!"); + ingredientService.addMark(markType, key, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(value = "mark", method = RequestMethod.DELETE) + public void delMark(@RequestParam String key) { + Assert.isTrue(!isAdmin(), "[参数错误]无操作权限!"); + Assert.isTrue(ingredientService.existsIngredientByKey(key), "[参数错误]食材不存在!"); + ingredientService.delMark(key, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(value = "excel", method = RequestMethod.PUT) + public void addIngredient(@RequestParam MultipartFile file) throws Exception { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.notNull(file, "[参数错误]请选择要导入的食材文件!"); + HttpServletResponse response = getResponse(); + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-Disposition", "attachment;filename*=utf-8''"+ URLEncoder.encode("[导入结果]" + file.getOriginalFilename(), "UTF-8")); + + ingredientService.upload(file.getInputStream(), response.getOutputStream(), getUid()); + } + + @ResponseBody + @RequestMapping(value = "excel", method = RequestMethod.GET) + public void template() throws Exception { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + HttpServletResponse response = getResponse(); + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + URLEncoder.encode("导入模板.xlsx", "UTF-8")); + + ingredientService.template(response.getOutputStream()); + } +} diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/MenuController.java b/diet-web/src/main/java/com/mathvision/diet/controller/MenuController.java new file mode 100644 index 0000000..9ef379b --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/MenuController.java @@ -0,0 +1,143 @@ +package com.mathvision.diet.controller; + +import com.google.common.collect.Lists; +import com.google.common.collect.Range; +import com.mathvision.diet.domian.MealType; +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.entity.Nutrition; +import com.mathvision.diet.service.MenuService; +import com.mathvision.diet.service.NutritionService; +import com.mathvision.diet.service.VenderService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.time.Instant; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@RequestMapping("/api/menu") +@Controller +public class MenuController extends BaseController { + + @Resource + VenderService venderService; + + @Resource + NutritionService nutritionService; + + @Resource + MenuService menuService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public List add(@RequestParam String name, @RequestParam(required = false) List vendors, @RequestParam Long nutrient, @RequestParam Integer day, @RequestParam List meals, @RequestParam List month, @RequestParam List crows) { + Assert.isTrue(StringUtils.isNotBlank(name), "[参数错误]食谱名称必填!"); + Assert.isTrue(Range.closed(1, 7).contains(day), "[参数错误]天数取值[1~7]!"); + Assert.isTrue(CollectionUtils.isNotEmpty(month) && month.stream().allMatch(x -> Range.closed(1, 12).contains(x)), "[参数错误]请选择正确的月份!"); + Assert.isTrue(CollectionUtils.isNotEmpty(meals), "[参数错误]餐次必填!"); + Assert.isTrue(meals.stream().allMatch(x -> MealType.toType(x) != null), "[参数错误]餐次取值[早餐,午餐,晚餐]!"); + + Nutrition nutrition = nutritionService.get(nutrient); + Assert.notNull(nutrition, "[参数错误]营养计划必选!"); + Set allCrows = nutrition.getIngredient().keySet(); + vendors = isAdmin() ? (vendors == null ? Lists.newArrayList() : vendors.stream().filter(venderService::exists).collect(Collectors.toList())) : Lists.newArrayList(getVender()); + + Assert.isTrue(CollectionUtils.isNotEmpty(vendors), "[参数错误]营养计划不适用于所选单位!"); + Assert.isTrue(new HashSet<>(nutrition.getVendors()).containsAll(vendors), "[参数错误]营养计划不适用于所选单位!"); + Assert.isTrue(CollectionUtils.isNotEmpty(crows), "[参数错误]人群取值[参照营养计划]必填!"); + Assert.isTrue(allCrows.containsAll(crows), "[参数错误]营养计划不包含所选的人群!"); + Instant dateTime = Instant.now(); + List menus = vendors.stream().map(v -> Menu.builder().name(name).meals(meals).crows(crows).scale(crows.stream().collect(Collectors.toMap(x -> x, x -> 0))).day(Long.valueOf(day)).nutrient(nutrient).month(month).vender(v).status(MenuStatus.draft).operate(getUid()).created(dateTime).modify(dateTime).build()).collect(Collectors.toList()); + return menuService.add(menus).stream().map(Menu::getId).collect(Collectors.toList()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delete(@RequestParam Long id) { + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() != MenuStatus.publish, "[参数错误]该食谱已发布,不可删除!"); + menuService.delete(id, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void update(@RequestParam Long id, @RequestParam(required = false) String name, @RequestParam(required = false) List month, @RequestParam Long nutrient, @RequestParam(required = false) Integer day, @RequestParam(required = false) List meals, @RequestParam(required = false) List crows) { + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() != MenuStatus.publish, "[参数错误]该食谱已发布,不可编辑!"); + Assert.isTrue(CollectionUtils.isEmpty(month) || month.stream().allMatch(x -> Range.closed(1, 12).contains(x)), "[参数错误]请选择正确的月份!"); + Assert.isTrue(day == null || Range.closed(1, 7).contains(day), "[参数错误]天数取值[1~7]!"); + + boolean flag = false; + boolean check = false; + if (StringUtils.isNotBlank(name) && !StringUtils.equals(menu.getName(), name)) { + menu.setName(name); + flag = true; + } + if (CollectionUtils.isNotEmpty(month) && !menu.getMonth().equals(month)) { + menu.setMonth(month); + flag = true; + } + if (CollectionUtils.isNotEmpty(meals) && !meals.equals(menu.getMeals())) { + Assert.isTrue(meals.stream().allMatch(x -> MealType.toType(x) != null), "[参数错误]餐次取值[早餐,午餐,晚餐]!"); + menu.setMeals(meals); + flag = true; + } + if (day != null && !day.equals(menu.getDay().intValue())) { + menu.setDay(day.longValue()); + flag = true; + } + if (nutrient != null && !nutrient.equals(menu.getNutrient())) { + menu.setNutrient(nutrient); + flag = true; + check = true; + } + if (CollectionUtils.isNotEmpty(crows) && !crows.equals(menu.getCrows())) { + menu.setCrows(crows); + check = true; + } + if (flag) { + if (check) { + Nutrition nutrition = nutritionService.get(menu.getNutrient()); + Assert.notNull(nutrition, "[参数错误]营养计划不存在!"); + Assert.isTrue(nutrition.getVendors().contains(menu.getVender()), "[参数错误]营养计划不适用于该企业!"); + Assert.isTrue(nutrition.getIngredient().keySet().containsAll(crows), "[参数错误]营养计划不包含所选的人群!"); + if (MapUtils.isNotEmpty(menu.getScale())) { + Map scale = menu.getScale(); + menu.setScale(menu.getCrows().stream().collect(Collectors.toMap(x -> x, x -> scale.getOrDefault(x, 0)))); + } + } + Instant dateTime = Instant.now(); + menu.setStatus(MenuStatus.draft); + menu.setOperate(getUid()); + menu.setModify(dateTime); + menuService.update(menu); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Object query(@RequestParam(required = false) Long id, @RequestParam(required = false) String name, @RequestParam(required = false) Long vender, @RequestParam(required = false) Long status, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + if (id != null) { + return isAdmin() ? menuService.get(id) : menuService.get(id, getVender()); + } + return menuService.list(isAdmin() ? vender : getVender(), name, MenuStatus.toType(status), parseDate(startTime), parseDate(endTime), PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/MenuDishController.java b/diet-web/src/main/java/com/mathvision/diet/controller/MenuDishController.java new file mode 100644 index 0000000..0230f36 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/MenuDishController.java @@ -0,0 +1,194 @@ +package com.mathvision.diet.controller; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.google.common.collect.Range; +import com.mathvision.diet.domian.MenuDishItemDTO; +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Dish; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.entity.MenuDish; +import com.mathvision.diet.service.*; +import com.mathvision.diet.vo.MenuDishVO; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.net.URLEncoder; +import java.time.Instant; +import java.time.LocalDate; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@RequestMapping("/api/menu/dish") +@Controller +public class MenuDishController extends BaseController { + + @Resource + EnumService enumService; + + @Resource + DishService dishService; + + @Resource + MenuService menuService; + + @Resource + MenuDishService menuDishService; + + @Resource + private IngredientService ingredientService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public Long add(@RequestParam Long menuId, @RequestParam Long dishId, @RequestParam Integer day, @RequestParam String meal, @RequestParam(required = false) String mark, @RequestParam String ingredient) { + Menu menu = checkAndConvert(menuId, MenuStatus.pass, MenuStatus.publish); + return menuDishService.add(checkAndConvert(menu, checkAndConvert(dishId), day, meal, mark, parseItems(ingredient, new HashSet<>(menu.getCrows())))).getId(); + } + + @ResponseBody + @RequestMapping(value = "batch", method = RequestMethod.PUT) + public List addAll(@RequestBody MenuDishVO menuDishVO) { + menuDishVO.getMenuIds().forEach(menuId -> checkAndConvert(menuId, MenuStatus.pass, MenuStatus.publish)); + return menuDishService.addAll(menuDishVO.getMenuIds().stream().map(menuId -> { + Menu menu = checkAndConvert(menuId, MenuStatus.pass, MenuStatus.publish); + return menuDishVO.getDishes().stream().map(dish -> checkAndConvert(menu, checkAndConvert(dish.getDish()), dish.getDay(), dish.getMeal(), dish.getMark(), dish.getItems().stream().map(item -> MenuDishItemDTO.builder().isMain(item.getIsMain()).key(item.getKey()).value(item.getValue()).build()).collect(Collectors.toList()))).collect(Collectors.toList()); + }).flatMap(List::stream).collect(Collectors.toList())).stream().map(MenuDish::getId).collect(Collectors.toList()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delete(@RequestParam(required = false) Long menuDishId, @RequestParam Long menuId) { + checkAndConvert(menuId, MenuStatus.pass, MenuStatus.publish); + if (menuDishId != null) { + MenuDish dish = isAdmin() ? menuDishService.get(menuDishId) : menuDishService.get(menuDishId, getVender()); + Assert.notNull(dish, "[参数错误]菜品不存在!"); + menuDishService.delete(menuDishId); + } else { + menuDishService.deleteAll(menuId); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void update(@RequestParam Long menuDishId, @RequestParam Long menuId, @RequestParam(required = false) String mark, @RequestParam(required = false) String ingredient) { + Menu menu = checkAndConvert(menuId, MenuStatus.pass, MenuStatus.publish); + MenuDish dish = isAdmin() ? menuDishService.get(menuDishId) : menuDishService.get(menuDishId, getVender()); + Assert.notNull(dish, "[参数错误]菜品不存在!"); + + boolean flag = false; + if (StringUtils.isNotBlank(mark) && !StringUtils.equals(mark, dish.getMarks())) { + Assert.isTrue(enumService.checkMark(mark), "[参数错误]菜品标签不在取值范围内!"); + dish.setMarks(mark); + flag= true; + } + if (StringUtils.isNotBlank(ingredient) && JSON.isValid(ingredient)) { + dish.setIngredient(parseItems(ingredient, new HashSet<>(menu.getCrows()))); + flag = true; + } + if (flag) { + Instant dateTime = Instant.now(); + dish.setOperate(getUid()); + dish.setModify(dateTime); + menuDishService.add(dish); + } + } + + + @ResponseBody + @RequestMapping(value = "export", method = RequestMethod.GET) + public void export(@RequestParam Long id) throws IOException { + Menu menu = isAdmin() ? menuService.get(id) : menuService.get(id, getVender()); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + + HttpServletResponse response = getResponse(); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + response.setHeader("Content-Disposition", "attachment;filename*=utf-8''"+ URLEncoder.encode("[食谱导出]" + menu.getId() + "-"+ menu.getName() + ".xlsx", "UTF-8")); + + menuDishService.export(menu, response.getOutputStream()); + } + + @ResponseBody + @RequestMapping(value = "analysis", method = RequestMethod.GET) + public JSONObject analysis(@RequestParam Long id, @RequestParam(required = false) Long day, @RequestParam(required = false) String crow) { + Menu menu = isAdmin() ? menuService.get(id) : menuService.get(id, getVender()); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + day = (day == null || day < 1 || day > menu.getDay()) ? (long) LocalDate.now().getDayOfWeek().getValue() : day; + crow = StringUtils.isBlank(crow) || !menu.getCrows().contains(crow) ? menu.getCrows().get(0) : crow; + List dishes = menuDishService.query(id, menu.getVender(), day); + return menuDishService.assess(menu, day, crow, dishes); + } + + @ResponseBody + @RequestMapping(value = "analysis/types", method = RequestMethod.GET) + public JSONObject types(@RequestParam Long id) { + Menu menu = isAdmin() ? menuService.get(id) : menuService.get(id, getVender()); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + List dishes = menuDishService.query(id, menu.getVender()); + return menuDishService.types(menu, dishes); + } + + @ResponseBody + @RequestMapping(value = "analysis/energy", method = RequestMethod.GET) + public JSONObject energy(@RequestParam Long id, @RequestParam(required = false) Long day, @RequestParam(required = false) String crow) { + Menu menu = isAdmin() ? menuService.get(id) : menuService.get(id, getVender()); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + day = (day == null || day < 1 || day > menu.getDay()) ? (long) LocalDate.now().getDayOfWeek().getValue() : day; + crow = StringUtils.isBlank(crow) || !menu.getCrows().contains(crow) ? menu.getCrows().get(0) : crow; + List dishes = menuDishService.query(id, menu.getVender(), day); + return menuDishService.energy(day, crow, dishes); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public List query(@RequestParam(required = false) Long menuId) { + if (menuId == null) { + Long currentMenuId = menuService.current(getVender()); + return menuDishService.query(currentMenuId, getVender(), (long) LocalDate.now().getDayOfWeek().getValue()); + } + return isAdmin() ? menuDishService.query(menuId) : menuDishService.query(menuId, getVender()); + } + + private MenuDish checkAndConvert(Menu menu, Dish dish, Integer day, String meal, String mark, List ingredient) { + Assert.isTrue(Range.closed(1, menu.getDay().intValue()).contains(day), "[参数错误]天数不在食谱的设置范围内!"); + Assert.isTrue(menu.getMeals().contains(meal), "[参数错误]餐次不在食谱的设置范围内!"); + + mark = StringUtils.isBlank(mark) ? dish.getMarks() : mark; + Assert.isTrue(enumService.checkMark(mark), "[参数错误]菜品标签不在取值范围内!"); + + Instant dateTime = Instant.now(); + return MenuDish.builder().vender(menu.getVender()).menu(menu.getId()).dish(dish.getId()).day(day.longValue()).meal(meal).name(dish.getName()).marks(mark).ingredient(ingredient).operate(getUid()).created(dateTime).modify(dateTime).build(); + } + + private Menu checkAndConvert(Long menuId, MenuStatus ... statuses) { + Menu menu = isAdmin() ? menuService.get(menuId) : menuService.get(menuId, getVender()); + Assert.notNull(menu, "[参数错误]食谱不存在, menuId:" + menuId); + if (statuses != null && statuses.length > 0) { + Assert.isTrue(Arrays.stream(statuses).noneMatch(menuStatus -> menu.getStatus().equals(menuStatus)), "[参数错误]食谱已通过审核或者发布, menuId:" + menuId); + } + return menu; + } + + private Dish checkAndConvert(Long dishId) { + Assert.notNull(dishId, "[参数错误]菜品不存在: dish=" + dishId); + Dish dish = isAdmin() ? dishService.get(dishId) : dishService.get(dishId, getVender()); + Assert.notNull(dish, "[参数错误]菜品不存在: dish=" + dishId); + return dish; + } + + private List parseItems(String ingredient, Set crows) { + Assert.isTrue(JSON.isValid(ingredient), "[参数错误]菜品成分解析失败!"); + List items = JSON.parseArray(ingredient, MenuDishItemDTO.class); + Assert.isTrue(items.stream().allMatch(item -> crows.containsAll(item.getValue().keySet())), "[参数错误]人群不在食谱的设置范围内!"); + Assert.isTrue(items.stream().allMatch(item -> ingredientService.existsIngredientByKey(item.getKey())), "[参数错误]请选择系统存在的食材!"); + return items; + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/MenuReleaseController.java b/diet-web/src/main/java/com/mathvision/diet/controller/MenuReleaseController.java new file mode 100644 index 0000000..ba19901 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/MenuReleaseController.java @@ -0,0 +1,58 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.service.MenuReleaseService; +import com.mathvision.diet.service.MenuService; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.util.Date; +import java.util.Map; + +@RequestMapping("/api/menu/release") +@Controller +public class MenuReleaseController extends BaseController { + @Resource + MenuService menuService; + @Resource + MenuReleaseService menuReleaseService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void publish(@RequestParam Long id, @RequestParam(required = false) Map scale, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime) { + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() == MenuStatus.pass, "[参数错误]该食谱当前非审批通过状态,不可发布!"); + menu.getScale().entrySet().forEach(x -> x.setValue(scale.getOrDefault(x.getKey(), 0))); + menuReleaseService.publish(id, menu.getScale(), parseDate(startTime, new Date()), parseDate(endTime, DateUtils.addDays(new Date(), menu.getDay().intValue())), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void cancel(@RequestParam Long id) { + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() == MenuStatus.publish, "[参数错误]该食谱非发布状态, 不可撤销发布!"); + menuReleaseService.cancel(id, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Page query(@RequestParam(required = false) String name, @RequestParam(required = false) Long vender, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + return menuReleaseService.list(vender, name, parseDate(startTime), parseDate(endTime), PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } +} + + diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/MenuReviewController.java b/diet-web/src/main/java/com/mathvision/diet/controller/MenuReviewController.java new file mode 100644 index 0000000..8c6e0ad --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/MenuReviewController.java @@ -0,0 +1,77 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domian.MenuStatus; +import com.mathvision.diet.entity.Menu; +import com.mathvision.diet.service.MenuReviewService; +import com.mathvision.diet.service.MenuService; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; + +@RequestMapping("/api/menu/review") +@Controller +public class MenuReviewController extends BaseController { + + @Resource + MenuService menuService; + + @Resource + MenuReviewService menuReviewService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void submit(@RequestParam Long id) { + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() == MenuStatus.draft || menu.getStatus() == MenuStatus.reject, "[参数错误]提交审核失败: 该食谱当前非草稿或者审核失败状态!"); + menuReviewService.submit(id, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void disable(@RequestParam Long id) { + Assert.isTrue(isAdmin(), "[参数错误]无审批权限!"); + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() != MenuStatus.pass && menu.getStatus() != MenuStatus.publish, "[参数错误]禁用食谱失败: 该食谱当前非审批通过或者发布状态!"); + menuReviewService.disable(id, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void approve(@RequestParam Long id, @RequestParam boolean pass, @RequestParam(required = false) String reason) { + Assert.isTrue(isAdmin(), "[参数错误]无审批权限!"); + Menu menu = menuService.get(id); + Assert.notNull(menu, "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getVender().equals(getVender()) || isAdmin(), "[参数错误]食谱不存在!"); + Assert.isTrue(menu.getStatus() == MenuStatus.submit || menu.getStatus() == MenuStatus.reject, "[参数错误]审批食谱失败: 该食谱当前非提交审核状态!"); + if(pass){ + menuReviewService.pass(id, reason, getUid()); + } else { + menuReviewService.reject(id, reason, getUid()); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Page query(@RequestParam(required = false) String name, @RequestParam(required = false) Integer status, @RequestParam(required = false) Long vender, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + return menuReviewService.list(vender, MenuStatus.toType(status), name, parseDate(startTime), parseDate(endTime), PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } + + @ResponseBody + @RequestMapping(value = "count", method = RequestMethod.GET) + public Object count() { + Assert.isTrue(isAdmin(), "[参数错误]无审批权限!"); + return menuReviewService.count(); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/NutritionController.java b/diet-web/src/main/java/com/mathvision/diet/controller/NutritionController.java new file mode 100644 index 0000000..cc3b36b --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/NutritionController.java @@ -0,0 +1,112 @@ +package com.mathvision.diet.controller; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.mathvision.diet.entity.Nutrition; +import com.mathvision.diet.service.NutritionService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@RequestMapping("/api/nutrition") +@Controller +public class NutritionController extends BaseController { + @Resource + NutritionService nutritionService; + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public Nutrition add(@RequestParam String name, @RequestParam List vendors, @RequestParam BigDecimal overflow) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.isTrue(StringUtils.isNotBlank(name), "[参数错误]营养计划名称必填!"); + Assert.notNull(overflow, "[参数错误]溢出范围必填!"); + Assert.isTrue(CollectionUtils.isNotEmpty(vendors), "[参数错误]单位列表必填!"); + Assert.isTrue(nutritionService.notExists(name), "[参数错误]营养计划名称已存在!"); + return nutritionService.add(Nutrition.builder().name(name).overflow(overflow).vendors(vendors).build(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delete(@RequestParam Long id) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.notNull(id, "[参数错误]营养计划必填!"); + Assert.isTrue(nutritionService.exists(id), "[参数错误]营养计划不存在!"); + + nutritionService.delete(id, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public Nutrition update(@RequestParam Long id, @RequestParam(required = false) String name, @RequestParam(required = false) BigDecimal overflow, @RequestParam(required = false) List vendors, @RequestParam(required = false) String foodCategoryDay, @RequestParam(required = false) String foodCategoryWeek, @RequestParam(required = false) String ingredient) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + Assert.isTrue(foodCategoryDay == null || JSON.isValid(foodCategoryDay), "[参数错误]日标准JSON解析错误!"); + Assert.isTrue(foodCategoryWeek == null || JSON.isValid(foodCategoryWeek), "[参数错误]周标准JSON解析错误!"); + Assert.isTrue(ingredient == null || JSON.isValid(ingredient), "[参数错误]食材JSON解析错误!"); + + Nutrition nutrition = nutritionService.get(id); + Assert.isTrue(nutrition != null, "[参数错误]营养计划不存在!"); + + boolean flag = false; + if(StringUtils.isNotBlank(name) && !StringUtils.equals(name, nutrition.getName()) && nutritionService.notExists(name)) { + nutrition.setName(name); + flag = true; + } + if(overflow != null && !nutrition.getOverflow().equals(overflow)) { + nutrition.setOverflow(overflow); + flag = true; + } + if(CollectionUtils.isNotEmpty(vendors)) { + nutrition.setVendors(vendors); + flag = true; + } + if (JSON.isValid(foodCategoryDay)) { + nutrition.setFoodCategoryDay(JSON.parseObject(foodCategoryDay, new TypeReference>(){})); + flag = true; + } + if (JSON.isValid(foodCategoryWeek)) { + nutrition.setFoodCategoryWeek(JSON.parseObject(foodCategoryWeek, new TypeReference>(){})); + flag = true; + } + if (JSON.isValid(foodCategoryWeek)) { + nutrition.setFoodCategoryWeek(JSON.parseObject(foodCategoryWeek, new TypeReference>(){})); + flag = true; + } + if (JSON.isValid(ingredient)) { + nutrition.setIngredient(JSON.parseObject(ingredient, new TypeReference>>>(){})); + flag = true; + } + if (flag) { + nutrition = nutritionService.update(nutrition, getUid()); + } + return nutrition; + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Page query(@RequestParam(required = false) String keyword, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + Assert.isTrue(isAdmin(), "[参数错误]无操作权限!"); + return nutritionService.list(keyword, PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } + + @ResponseBody + @RequestMapping(value="select", method = RequestMethod.GET) + public Object query(@RequestParam(required = false) Long id, @RequestParam(required = false) String keyword, @RequestParam(required = false) Long vender) { + if (id != null) { + return nutritionService.get(id); + } + return nutritionService.query(isAdmin() ? vender : getVender(), keyword); + } +} diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/RoleController.java b/diet-web/src/main/java/com/mathvision/diet/controller/RoleController.java new file mode 100644 index 0000000..b1d4bf9 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/RoleController.java @@ -0,0 +1,53 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domian.AuthType; +import com.mathvision.diet.domian.RoleType; +import com.mathvision.diet.entity.Role; +import com.mathvision.diet.entity.RoleItem; +import com.mathvision.diet.service.UserService; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("/api/role") +@Controller +public class RoleController extends BaseController { + + @Resource + private UserService userService; + + @ResponseBody + @RequestMapping(value = "item") + public List listRoleItems() { + return userService.listRoleItems(isAdmin() ? AuthType.SERVER : AuthType.CLIENT); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void addRole(@RequestParam String roleName, @RequestParam(required = false) List items) { + userService.addRole(roleName, items, RoleType.CUSTOM, isAdmin() ? AuthType.SERVER : AuthType.CLIENT, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delRole(@RequestParam Long roleId) { + userService.delRole(roleId, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void modRole(@RequestParam Long roleId, @RequestParam(required = false) String roleName, @RequestParam(required = false) List items) { + userService.changeRole(roleId, roleName, items, isAdmin() ? AuthType.SERVER : AuthType.CLIENT, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public List listRole() { + return userService.listRole(getVender()); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/UserController.java b/diet-web/src/main/java/com/mathvision/diet/controller/UserController.java new file mode 100644 index 0000000..f6ca264 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/UserController.java @@ -0,0 +1,54 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.util.List; + +@RequestMapping("/api/user") +@Controller +public class UserController extends BaseController { + + @Resource + private UserService userService; + + @ResponseBody + @RequestMapping(value = "check") + public boolean check(@RequestParam String uid) { + return userService.checkUser(uid); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void addUser(@RequestParam String uid, @RequestParam String name, @RequestParam String password, @RequestParam Long roleId) { + userService.addUser(uid, name, password, roleId, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delUser(@RequestParam String uid) { + userService.delUser(uid, getVender(), getUid()); + if (StringUtils.isBlank(uid)) { + delSession(); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void modUser(@RequestParam String uid, @RequestParam(required = false) String name, @RequestParam(required = false) String password, @RequestParam(required = false) Long roleId) { + userService.changeUser(uid, name, password, roleId, getVender(), getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public List listUser() { + return userService.listUser(getVender()); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/controller/VenderController.java b/diet-web/src/main/java/com/mathvision/diet/controller/VenderController.java new file mode 100644 index 0000000..73ceedb --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/controller/VenderController.java @@ -0,0 +1,164 @@ +package com.mathvision.diet.controller; + +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.domian.VenderType; +import com.mathvision.diet.entity.Vender; +import com.mathvision.diet.entity.VenderConfig; +import com.mathvision.diet.service.UserService; +import com.mathvision.diet.service.VenderService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.stereotype.Controller; +import org.springframework.util.Assert; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.Instant; +import java.util.Date; +import java.util.List; + +@RequestMapping("/api/vender") +@Controller +public class VenderController extends BaseController { + + @Resource + private VenderService venderService; + + @Resource + private UserService userService; + + @ResponseBody + @RequestMapping(value = "check/account") + public boolean checkAccount(@RequestParam String account) { + return venderService.checkAccount(account); + } + + @ResponseBody + @RequestMapping(value = "check/name") + public boolean checkName(@RequestParam String name) { + return venderService.checkName(name); + } + + @ResponseBody + @RequestMapping(value = "config", method = RequestMethod.GET) + public VenderConfig queryConfig() { + Assert.isTrue(!isAdmin(), "[无权限]仅业务端可以操作!"); + return venderService.queryConfig(getVender()); + } + + @ResponseBody + @RequestMapping(value = "config", method = RequestMethod.POST) + public void modConfig(@RequestParam BigDecimal breakfast, @RequestParam BigDecimal lunch, @RequestParam BigDecimal dinner) { + Assert.isTrue(!isAdmin(), "[无权限]仅业务端可以操作!"); + venderService.modConfig(getVender(), breakfast, lunch, dinner, getUid()); + } + @ResponseBody + @RequestMapping(method = RequestMethod.PUT) + public void addVender(@RequestParam String account, @RequestParam String password, @RequestParam String name, @RequestParam String category, @RequestParam @DateTimeFormat(pattern="yyyy-MM-dd") Date expire, @RequestParam(required = false) String icon, @RequestParam(required = false) String address, @RequestParam(required = false) String contacts, @RequestParam(required = false) String phone, @RequestParam(required = false) String email) { + VenderType venderType = VenderType.toType(category); + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + Assert.notNull(venderType, "[参数错误]单位类型必选!"); + Assert.isTrue(StringUtils.isNotBlank(password), "[参数错误]初始密码必填!"); + Assert.isTrue(Instant.now().isBefore(expire.toInstant()), "[参数错误]过期时间不能小于现在!"); + Assert.isTrue(StringUtils.isNotBlank(name) && checkName(name), "[参数错误]单位名称必填,且不能重复!"); + Assert.isTrue(StringUtils.isNotBlank(account) && checkAccount(account), "[参数错误]账号必填,且不能重复!"); + Vender vender = Vender.builder().account(account).name(name).category(venderType).expire(expire.toInstant()).icon(icon).address(address).phone(phone).email(email).contacts(contacts).build(); + venderService.addVender(vender, password, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.DELETE) + public void delVender(@RequestParam Long venderId) { + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + venderService.delVender(venderId, getUid()); + } + + @ResponseBody + @RequestMapping(method = RequestMethod.POST) + public void modVender(@RequestParam Long venderId, @RequestParam(required = false) String account, @RequestParam(required = false) String name, @RequestParam(required = false) String category, @RequestParam(required = false) @DateTimeFormat(pattern="yyyy-MM-dd") Date expire, @RequestParam(required = false) Boolean status, @RequestParam(required = false) String icon, @RequestParam(required = false) String address, @RequestParam(required = false) String contacts, @RequestParam(required = false) String phone, @RequestParam(required = false) String email) { + venderId = auth(venderId); + VenderType venderType = VenderType.toType(category); + boolean change = false; + Vender vender = venderService.queryVender(venderId); + Assert.isTrue(vender != null, "[参数错误]单位不存在!"); + Assert.isTrue(vender.getAccount().equals(getUid()) || isAdmin(), "[无权限]仅主账号或者系统管理员可以操作!"); + + if (expire != null && !expire.toInstant().equals(vender.getExpire()) && Instant.now().isBefore(expire.toInstant())) { + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + vender.setExpire(expire.toInstant()); + change = true; + } + if(status != null && !status.equals(vender.getStatus())) { + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + vender.setStatus(status); + change = true; + } + if (StringUtils.isNotBlank(name) && !StringUtils.equals(name, vender.getName()) && checkName(name)) { + vender.setName(name); + change = true; + } + if(venderType != null && !venderType.equals(vender.getCategory())) { + vender.setCategory(venderType); + change = true; + } + if (StringUtils.isNotBlank(account) && !StringUtils.equals(account, vender.getAccount())) { + UserDO userDO = userService.queryUser(account); + Assert.isTrue(userDO != null && venderId.equals(userDO.getVender().getId()), "[参数错误]绑定账户不存在!"); + Long adminRoleId = isAdmin() ? userService.queryUser(vender.getAccount()).getRoleId() : getRoleId(); + Assert.isTrue(adminRoleId != null, "[参数错误]单位管理员角色缺失,请联系系统管理员处理!"); + if (!adminRoleId.equals(userDO.getRoleId())) { + userService.changeUser(account, null, null, adminRoleId, venderId, getUid()); + } + vender.setAccount(account); + change = true; + } + if(icon != null && !StringUtils.equals(icon, vender.getIcon())) { + vender.setIcon(icon); + change = true; + } + if(icon != null && !StringUtils.equals(icon, vender.getIcon())) { + vender.setIcon(icon); + change = true; + } + if(phone != null && !StringUtils.equals(phone, vender.getPhone())) { + vender.setPhone(phone); + change = true; + } + if(email != null && !StringUtils.equals(email, vender.getEmail())) { + vender.setEmail(email); + change = true; + } + if(contacts != null && !StringUtils.equals(contacts, vender.getContacts())) { + vender.setContacts(contacts); + change = true; + } + if(address != null && !StringUtils.equals(address, vender.getAddress())) { + vender.setAddress(address); + change = true; + } + if (change) { + venderService.modVender(vender, getUid()); + } + } + + @ResponseBody + @RequestMapping(method = RequestMethod.GET) + public Page pageVender(@RequestParam(required = false) String keyword, @RequestParam(required = false) String category, @RequestParam(required = false, defaultValue = "0") int pageNo, @RequestParam(required = false, defaultValue = "20") int pageSize) { + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + return venderService.pageVender(keyword, VenderType.toType(category), PageRequest.of(pageNo, pageSize).withSort(Sort.by(Sort.Direction.DESC, "id"))); + } + + @ResponseBody + @RequestMapping(value = "select", method = RequestMethod.GET) + public List listVender(@RequestParam(required = false) String keyword, @RequestParam(required = false) List vendors) { + Assert.isTrue(isAdmin(), "[无权限]仅系统管理员可以操作!"); + return venderService.listVender(keyword, vendors); + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/domain/Result.java b/diet-web/src/main/java/com/mathvision/diet/domain/Result.java new file mode 100644 index 0000000..21cfcc9 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/domain/Result.java @@ -0,0 +1,64 @@ +package com.mathvision.diet.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; + +/** + * 统一输出定义 + * + * @author caoyiwen + * @creation 2015年9月2日 + */ +@ToString +public class Result implements Serializable { + + private static final long serialVersionUID = 1L; + public static final Result NOT_SUPPORT = new Result(ResultCode.not_support_operate); + public static final Result NOT_PRIVILEGED = new Result(ResultCode.not_privileged); + public static final Result INVALID_USER_PASS = new Result(ResultCode.invalid_user_password); + public static final Result EXPIRED = new Result(ResultCode.expired_vender); + public static final Result ILLEGAL_ARGUMENT = new Result(ResultCode.illegal_argument); + public static final Result FAILURE = new Result(ResultCode.operate_failure); + public static final Result ERROR = new Result(ResultCode.system_error); + public static final Result NOT_LOGIN = new Result(ResultCode.need_login); + public static final Result SUCCESS = new Result(ResultCode.success); + + @Getter + @Setter + private int code; + @Getter + @Setter + private String desc; + @Getter + @Setter + private Object body; + + public Result(ResultCode resultCode) { + setCode(resultCode.getCode()); + setDesc(resultCode.getDesc()); + } + + public Result(int code, String msg) { + setCode(code); + setDesc(msg); + } + + /** + * 使用通用结果码生成对象 + */ + public Result(ResultCode resultCode, Object body) { + setCode(resultCode.getCode()); + setDesc(resultCode.getDesc()); + setBody(body); + } + + /** + * 判断执行结果是否成功 + */ + public boolean isSuccess() { + return ResultCode.success.getCode() == code; + } +} diff --git a/diet-web/src/main/java/com/mathvision/diet/domain/ResultCode.java b/diet-web/src/main/java/com/mathvision/diet/domain/ResultCode.java new file mode 100644 index 0000000..ad44858 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/domain/ResultCode.java @@ -0,0 +1,35 @@ +package com.mathvision.diet.domain; + +import lombok.Getter; + +/** + * 统一结果码定义 + * + * @author caoyiwen + * @creation 2015年9月2日 + */ +public enum ResultCode { + + success(200, "成功"), + + invalid_user_password(300, "用户名或者密码错误!"), + + expired_vender(300, "账户过期,请联系管理员续费!"), + + illegal_argument(400, "参数错误!"), + need_login(401, "未登录!"), + not_support_operate(404, "不支持的请求!"), + not_privileged(405, "无权限执行该操作!"), + system_error(500, "系统异常!"), + operate_failure(503, "操作失败!"); + + @Getter + private final int code; + @Getter + private final String desc; + + ResultCode(int code, String desc) { + this.code = code; + this.desc = desc; + } +} diff --git a/diet-web/src/main/java/com/mathvision/diet/exception/DietException.java b/diet-web/src/main/java/com/mathvision/diet/exception/DietException.java new file mode 100644 index 0000000..23c15a7 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/exception/DietException.java @@ -0,0 +1,15 @@ +package com.mathvision.diet.exception; + +import com.mathvision.diet.domain.Result; +import lombok.Getter; + +@Getter +public class DietException extends RuntimeException{ + + private final Result result; + + public DietException(Result result) { + super(result.getDesc()); + this.result = result; + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/utils/JWTUtils.java b/diet-web/src/main/java/com/mathvision/diet/utils/JWTUtils.java new file mode 100644 index 0000000..83bcbcf --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/utils/JWTUtils.java @@ -0,0 +1,49 @@ +package com.mathvision.diet.utils; + +import com.alibaba.fastjson.JSON; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.mathvision.diet.constant.Constant; +import com.mathvision.diet.domain.UserDO; +import com.mathvision.diet.entity.RoleItem; +import com.mathvision.diet.entity.Vender; +import lombok.extern.slf4j.Slf4j; + +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class JWTUtils { + private static final String SECRET = "!Q@W#E$R_MATHVISION_###"; + + public static String getToken(UserDO userDO) { + return JWT.create().withClaim("isAdmin", userDO.getVender() == null) + .withClaim("uid", userDO.getUid()) + .withClaim("rid", userDO.getRoleId()) + .withClaim("items", JSON.toJSONString(userDO.getRoleItems().stream().map(RoleItem::getId).collect(Collectors.toList()))) + .withClaim("vender", userDO.getVender() == null ? null : userDO.getVender().getId()) + .withExpiresAt(new Date(System.currentTimeMillis() + Constant.TOKEN_EXPIRE_SECOND * 1000)) + .sign(Algorithm.HMAC256(SECRET)); + } + + /** + * 验证token 合法性 + */ + public static UserDO verify(String token) { + try { + DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token); + boolean isAdmin = decodedJWT.getClaim("isAdmin").asBoolean(); + String uid = decodedJWT.getClaim("uid").asString(); + Long rid = decodedJWT.getClaim("rid").asLong(); + Vender vender = Vender.builder().id(decodedJWT.getClaim("vender").isMissing() ? null : decodedJWT.getClaim("vender").asLong()).build(); + List roleItems = JSON.parseArray(decodedJWT.getClaim("items").asString(), Long.class).stream().map(id -> RoleItem.builder().id(id).build()).collect(Collectors.toList()); + UserDO userDO = UserDO.builder().uid(uid).roleId(rid).roleItems(roleItems).vender(vender).build(); + return userDO == null || isAdmin != userDO.isAdmin() ? null : userDO; + } catch (Exception e) { + log.error("[JWTUtils] verify exception :" + e.getMessage(), e); + return null; + } + } +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishDetailVO.java b/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishDetailVO.java new file mode 100644 index 0000000..b1c8961 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishDetailVO.java @@ -0,0 +1,26 @@ +package com.mathvision.diet.vo; + +import com.mathvision.diet.domian.MenuDishItemDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MenuDishDetailVO { + /** 用于那天*/ + Integer day; + /** 用于那餐*/ + String meal; + /**打标,默认用原菜品标签*/ + String mark; + /**引用的菜品编号 */ + Long dish; + /**人群食材对应关系*/ + List items; +} \ No newline at end of file diff --git a/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishVO.java b/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishVO.java new file mode 100644 index 0000000..8a80ee7 --- /dev/null +++ b/diet-web/src/main/java/com/mathvision/diet/vo/MenuDishVO.java @@ -0,0 +1,17 @@ +package com.mathvision.diet.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MenuDishVO { + private List menuIds; + private List dishes; +} \ No newline at end of file diff --git a/diet-web/src/main/resources/application-dev.yml b/diet-web/src/main/resources/application-dev.yml new file mode 100644 index 0000000..15d3a10 --- /dev/null +++ b/diet-web/src/main/resources/application-dev.yml @@ -0,0 +1,17 @@ +spring: + datasource: + url: jdbc:log4jdbc:mysql://localhost:3306/diet?useUnicode=true&characterEncoding=utf-8 + username: root + password: 123456 + driver-class-name: net.sf.log4jdbc.DriverSpy + jpa: + show-sql: false + hibernate: + ddl-auto: none + +logging: + level: + jdbc.resultset: off + jdbc.audit: off + jdbc.sqlonly: off + jdbc.connection: off \ No newline at end of file diff --git a/diet-web/src/main/resources/application-prod.yml b/diet-web/src/main/resources/application-prod.yml new file mode 100644 index 0000000..eaaa42a --- /dev/null +++ b/diet-web/src/main/resources/application-prod.yml @@ -0,0 +1,10 @@ +spring: + datasource: + url: jdbc:mysql://47.109.27.8:3306/diet?useUnicode=true&characterEncoding=utf-8 + username: admin + password: '@Jiluo2019' + driver-class-name: com.mysql.cj.jdbc.Driver + jpa: + show-sql: false + hibernate: + ddl-auto: none \ No newline at end of file diff --git a/diet-web/src/main/resources/application.yml b/diet-web/src/main/resources/application.yml new file mode 100644 index 0000000..242a213 --- /dev/null +++ b/diet-web/src/main/resources/application.yml @@ -0,0 +1,23 @@ +spring: + application: + name: diet + profiles: + active: prod + web: + resources: + static-locations: classpath:static + jpa: + database-platform: org.hibernate.dialect.MySQLDialect + open-in-view: false + +session: + invalid: 600 + +server: + port: 9527 + +logging: + config: classpath:logback-spring.xml + +file: + encoding: utf-8 \ No newline at end of file diff --git a/diet-web/src/main/resources/logback-spring.xml b/diet-web/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..696da48 --- /dev/null +++ b/diet-web/src/main/resources/logback-spring.xml @@ -0,0 +1,137 @@ + + + + + + + box + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${log.level} + + + ${CONSOLE_LOG_PATTERN} + + UTF-8 + + + + + + + + ${log.path}/${log.name}/${log.name}-info.log + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %.-${log.length}msg%n + UTF-8 + + + + ${log.path}/${log.name}/${log.name}-info-%d{yyyy-MM-dd}.%i.log + + ${log.max.file} + + ${log.max.history} + + ${log.max.size} + + + + INFO + ACCEPT + DENY + + + + + + ${log.path}/${log.name}/${log.name}-warn.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %.-${log.length}msg%n + UTF-8 + + + ${log.path}/${log.name}/${log.name}-warn-%d{yyyy-MM-dd}.%i.log + ${log.max.file} + ${log.max.history} + ${log.max.size} + + + WARN + ACCEPT + DENY + + + + + + ${log.path}/${log.name}/${log.name}-error.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %.-${log.length}msg%n + UTF-8 + + + ${log.path}/${log.name}/${log.name}-error-%d{yyyy-MM-dd}.%i.log + ${log.max.file} + ${log.max.history} + ${log.max.size} + + + ERROR + ACCEPT + DENY + + + + + + ${log.path}/${log.name}/${log.name}-protocol.log + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %.-${log.length}msg%n + UTF-8 + + + ${log.path}/${log.name}/${log.name}-protocol-%d{yyyy-MM-dd}.%i.log + ${log.max.file} + ${log.max.history} + ${log.max.size} + + + + + + + + + + + + + + \ No newline at end of file diff --git a/diet-web/src/main/resources/static/basic.html b/diet-web/src/main/resources/static/basic.html new file mode 100644 index 0000000..9746d3a --- /dev/null +++ b/diet-web/src/main/resources/static/basic.html @@ -0,0 +1,355 @@ +

+

1. ¼

+

GET /user/login?uid=xxx&pwd=BE56E057F20F883E

+
+

MD5ܺдȡ16λʾԭΪ123456

+
+

:

+
{
+  "body": {
+    "roleName": "Ա",
+    "uid": "xxx",
+    "admin": false,  // Ƿ,true-ǹ, false-ҵ
+    "name": "ҵ˲˺",
+    "phone": "13919103409",
+    "roleId": 2,
+    "roleItems": [
+      {
+        "category": "Ȩ",
+        "id": 18,
+        "itemName": "ʹ",
+        "itemType": "ҵ"
+      }
+    ],
+    "vender": {
+      "account": "13919103408",
+      "address": "·",
+      "contacts": "",
+      "expire": 1693651185000,
+      "icon": "data:image/jpeg;base64,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",
+      "id": 1,  // λ,Ҫλ
+      "name": "ɶʵСѧ",
+      "phone": "13919103408",
+      "status": true
+    }
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. dz

+

GET /user/logout

+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ޸Լ

+

POST /api/password

+
+

Content-Type:application/x-www-form-urlencoded

+
+

:

+
oldPassword=BE56E057F20F883E   // ԭ
+password=BE56E057F20F883E      // 
+
+

:

+
{
+"code": 200,
+"desc": "ɹ",
+"success": true
+}
+
+

4. ȡöϢ

+

GET /api/enum

+

:

+
{
+  "body": {
+    "nutrient": [
+      {
+        "key": "fiber",
+        "measurement": "g",
+        "nrv": 25.00,
+        "value": "ʳά"
+      },
+      {
+        "key": "calcium",
+        "measurement": "mg",
+        "nrv": 800.00,
+        "value": ""
+      },
+      {
+        "key": "vb1",
+        "measurement": "mg",
+        "nrv": 1.40,
+        "value": "άB1"
+      },
+      {
+        "key": "carbs",
+        "measurement": "g",
+        "nrv": 300.00,
+        "value": "̼ˮ"
+      },
+      {
+        "key": "vb2",
+        "measurement": "mg",
+        "nrv": 1.40,
+        "value": "άB2"
+      },
+      {
+        "key": "va",
+        "measurement": "gRAE",
+        "nrv": 800.00,
+        "value": "άA"
+      },
+      {
+        "key": "vc",
+        "measurement": "mg",
+        "nrv": 100.00,
+        "value": "άC"
+      },
+      {
+        "key": "protein",
+        "measurement": "g",
+        "nrv": 60.00,
+        "value": ""
+      },
+      {
+        "key": "fat",
+        "measurement": "g",
+        "nrv": 60.00,
+        "value": "֬"
+      },
+      {
+        "key": "iron",
+        "measurement": "mg",
+        "nrv": 15.00,
+        "value": ""
+      },
+      {
+        "key": "zinc",
+        "measurement": "mg",
+        "nrv": 15.00,
+        "value": "п"
+      },
+      {
+        "key": "energy",
+        "measurement": "kcal",
+        "nrv": 2000.00,
+        "value": "kcal"
+      }
+    ],
+    "menuStatus": [
+      {
+        "key": 0,
+        "value": "ݸ"
+      },
+      {
+        "key": 1,
+        "value": "ύ"
+      },
+      {
+        "key": 2,
+        "value": "ͨ"
+      },
+      {
+        "key": 3,
+        "value": "ʧ"
+      },
+      {
+        "key": 4,
+        "value": ""
+      },
+      {
+        "key": 5,
+        "value": ""
+      }
+    ],
+    "markType": [
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      }
+    ],
+    "mealType": [
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      }
+    ],
+    "category": [
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "༰Ʒ",
+        "value": "༰Ʒ"
+      },
+      {
+        "key": "Ӥ׶ʳƷ",
+        "value": "Ӥ׶ʳƷ"
+      },
+      {
+        "key": "Ϻ",
+        "value": "Ϻ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "ƾ",
+        "value": "ƾ"
+      },
+      {
+        "key": "ζƷ",
+        "value": "ζƷ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "߲",
+        "value": "߲"
+      },
+      {
+        "key": "ˮ",
+        "value": "ˮ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "Сԡ",
+        "value": "Сԡ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "̼Ʒ",
+        "value": "̼Ʒ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "ʳʳƷ",
+        "value": "ʳʳƷ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      }
+    ],
+    "mark": [
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "ˮ",
+        "value": "ˮ"
+      },
+      {
+        "key": "ʳ",
+        "value": "ʳ"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "λ",
+        "value": "λ"
+      },
+      {
+        "key": "ʳ",
+        "value": "ʳ"
+      },
+      {
+        "key": "ʳ",
+        "value": "ʳ"
+      },
+      {
+        "key": "ز",
+        "value": "ز"
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      },
+      {
+        "key": "",
+        "value": ""
+      }
+    ],
+    "venderType": [
+      {
+        "key": "ѧУ",
+        "value": "ѧУ"
+      },
+      {
+        "key": "ҽԺ",
+        "value": "ҽԺ"
+      },
+      {
+        "key": "ҵλ",
+        "value": "ҵλ"
+      },
+      {
+        "key": "",
+        "value": ""
+      }
+    ]
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/change.html b/diet-web/src/main/resources/static/change.html new file mode 100644 index 0000000..64b90e8 --- /dev/null +++ b/diet-web/src/main/resources/static/change.html @@ -0,0 +1,30 @@ +

޸ļ¼

+

9.10

+
    +
  • Э
  • +
  • ûЭ
  • +
  • ɫЭ
  • +
  • λЭ
  • +
+

9.11

+
    +
  • ûбЭ鷵ֶʱ:time
  • +
  • ӽɫЭеȨбΪ
  • +
  • ɫбЭ鷵صȨַλ
  • +
+

9.17

+
    +
  • ʳĽӿ: markӿڵ(PUT, DELETE) ingredientDELETEָ£
  • +
  • λӿ: venderӿڵ(GET) keyword,Ϊҳѯ + /api/vender/select(GET), ڹѡλ
  • +
  • λӿ: Ӻ޸categoryֶΣλ, ѯӿselect֧ѯλϢ
  • +
  • öٽӿ: ӵλöȡֵΧ
  • +
  • ʳĽӿ: selectӿѯ, صģӿ, ݵӿ
  • +
  • Ʒӿ: Ӫǩӿ
  • +
+

9.23

+
    +
  • Ʒӿ: ʳбݽṹǷ(boolean)
  • +
  • öٽӿ: Ӳʹöٺʳ״̬ö
  • +
+ diff --git a/diet-web/src/main/resources/static/dish.html b/diet-web/src/main/resources/static/dish.html new file mode 100644 index 0000000..7d3431e --- /dev/null +++ b/diet-web/src/main/resources/static/dish.html @@ -0,0 +1,239 @@ +

Ʒ

+

1. ѯƷ(ģѯ)

+
+

GET /api/dish

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+keyword=Ѽ   // ѯؼ
+mark=     // ǩ  ȡֵGET /api/basic/enum ӿе mark
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "icon": "data:image/png;base64,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",
+        "id": 2,
+        "ingredient": [
+          {
+            "isMain": true,
+            "key": "011101",
+            "value": 500
+          }
+        ],
+        "marks": "ʳ",
+        "modify": 1695277128000,
+        "month": [
+          1,
+          2,
+          3,
+          4,
+          5,
+          6,
+          7,
+          8,
+          9,
+          10,
+          1,
+          12
+        ],
+        "name": "ѳ",
+        "vender": 4
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 2,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ѯƷ(IDȷѯ)

+
+

GET /api/dish

+
+

:

+
id=1 
+
+

:

+
{
+    "icon": "data:image/png;base64,iVBORkJggg==",
+    "id": 1,
+    "ingredient": [
+      {
+        "isMain": true,
+        "key": "011101",
+        "value": 500
+      }
+    ],
+    "marks": "",
+    "month": [
+      1,
+      3,
+      5,
+      7,
+      9,
+      12
+    ],
+    "name": "Ѽ",
+    "vender": 1
+}
+
+

3. ģѯ(ʳײѡƷ)

+
+

GET /api/dish/select

+
+

:

+
keyword= 
+
+

:

+
{
+  "body": [
+    {
+      "id": 25,
+      "ingredient": [
+        {
+          "key": "3355",
+          "value": 22
+        },
+        {
+          "key": "3378",
+          "value": 111
+        }
+      ],
+      "marks": "ʳ",
+      "name": "Ŷ"
+    },
+    {
+      "id": 24,
+      "ingredient": [
+        {
+          "isMain": false,
+          "key": "3355",
+          "value": 22
+        },
+        {
+          "isMain": true,
+          "key": "3378",
+          "value": 111
+        }
+      ],
+      "marks": "ʳ",
+      "name": "Ŷ"
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. Ӫǩ

+
+

GET /api/dish/label

+
+

:

+
ids=1,2,3   // IDָǩвƷıǩб
+
+

:

+
{
+  "body": [
+    {
+      "component": [
+        {
+          "name": "",
+          "nutrition": "60.00(mg)",
+          "nvr": "0.00%"
+        },
+        {
+          "name": "vitamin-a",
+          "nutrition": "115.00(-)",
+          "nvr": "-"
+        },
+        {
+          "name": "",
+          "nutrition": "75.00(g)",
+          "nvr": "1.00%"
+        },
+        {
+          "name": "֬",
+          "nutrition": "50.00(g)",
+          "nvr": "1.00%"
+        },
+        {
+          "name": "kcal",
+          "nutrition": "50.00(kcal)",
+          "nvr": "0.00%"
+        }
+      ],
+      "ingredients": [
+        "С"
+      ],
+      "name": "ѳ"
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ӲƷ

+
+

PUT /api/dish

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+name=ѳ      //   
+vendors=1,2,3      // λб, ˱ҵû
+icon=              // ͼƬ
+month=1,2,3       //·
+mark=          //   ǩ  ȡֵGET /api/basic/enum ӿе mark
+ingredient=[{"key": "011101", "value": 500, "isMain": true}]   // ʳб
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ޸

+
+

POST /api/dish

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+id=1               // 
+name=ѳ      //   
+vendors=1,2,3      // λб
+icon=              // ͼƬ
+month=1,2,3        // ·
+mark=           // ǩ  ȡֵGET /api/basic/enum ӿе mark
+ingredient=[{"key": "011101", "value": 500, "isMain": true}]   // ʳб
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. ɾ

+
+

DELETE /api/dish

+
+

:

+

Content-Type:application/x-www-form-urlencoded +ids=9,10 //

+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/index.html b/diet-web/src/main/resources/static/index.html new file mode 100644 index 0000000..9808815 --- /dev/null +++ b/diet-web/src/main/resources/static/index.html @@ -0,0 +1,44 @@ +

协议约定

+
+

协议格式: restfull + json + utf-8

+

协议格式中,凡是用 * 标识字段均为必须字段,否则为可选字段。

+

密码:协议中涉及password字段全部使用16位的MD5加密传输(MD5加密后取后16位,大写)

+
+

协议列表

+ +

响应示例

+
{
+    "body": {},
+    "code": 1,
+    "desc": "成功"
+}
+
+

返回码表

+
基础返回码:
+    success               (200, "成功"),
+
+    invalid_user_password (300, "用户名或者密码错误!"),
+
+    expired_vender        (301, "账户过期,请联系管理员续费!"),
+
+    illegal_argument      (400, "参数错误!"),
+    need_login            (401, "未登录!"),
+    not_support_operate   (404, "不支持的�
+ diff --git a/diet-web/src/main/resources/static/ingredient.html b/diet-web/src/main/resources/static/ingredient.html new file mode 100644 index 0000000..bf70ab5 --- /dev/null +++ b/diet-web/src/main/resources/static/ingredient.html @@ -0,0 +1,181 @@ +

ʳIJ

+

1. ѯʳ

+
+

GET /api/ingredient

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+keyword=01   // ѯؼ֣ģƥ
+type=   // ʳ
+mark=     // ʳı. ҵñǣû
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "key": "011101",
+        "mark": "",
+        "name": "С",
+        "nutrient": {
+          "fat": 10,
+          "energy": 10,
+          "calcium": 12,
+          "protein": 15,
+          "vitamin-a": 23
+        },
+        "time": 1693759354000,
+        "type": ""
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ѯʳ(IDѯ)

+
+

GET /api/ingredient/select

+
+

:

+
keys=011101,011102,011103 // ID ѯ2ѡһ
+keyword=01       // ѯؼ֣ģƥ
+
+

:

+
{
+  "body": [
+    {
+      "key": "011101",
+      "name": "С",
+      "type": ""
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ʳ(˽ӿ)

+
+

PUT /api/ingredient

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+key=010101   // 
+name=ʳ  // 
+type=    //   ȫ   ȡֵΧ(/api/basic/enum) category
+nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23}
+//  ȡֵΧ(/api/basic/enum) nutrient
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ޸ʳ(˽ӿ)

+
+

POST /api/ingredient

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+key=010101   // 
+name=ʳ
+type=
+nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23}
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ɾʳ(˽ӿ)

+
+

DELETE /api/ingredient

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+keys=010101,0101012,0101013  // 
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ʳĴ(ҵ˽ӿ)

+
+

PUT /api/ingredient/mark

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+key=010101 // ʳı
+mark=          // , ȡֵ:  /
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. ȡ(ҵ˽ӿ)

+
+

DELETE /api/ingredient/mark

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+key=010101    // ʳı
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

8. (˽ӿ)

+
+

PUT http://localhost:9527/api/ingredient/excel

+
+

:

+
Content-Type: multipart/form-data; boundary=boundary
+
+--boundary
+Content-Disposition: form-data; name="file"; filename="a.xlsx"
+
+< C:\Users\CCC\Documents\WeChat Files\wxid_40aqnb839lkd12\FileStorage\File\2023-09\PƷ.xlsx
+
+--boundary
+Content-Disposition: form-data; name="extraInfo";
+
+

:

+
Content-Disposition: attachment;filename*=utf-8''%5B%E5%AF%BC%E5%85%A5%E7%BB%93%E6%9E%9C%5Da.xlsx
+Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8
+Transfer-Encoding: chunked
+Date: Sun, 17 Sep 2023 18:49:10 GMT
+Keep-Alive: timeout=60
+Connection: keep-alive
+
+Response file saved.
+> []a-2.xlsx
+
+

9.صģ

+
+

GET /api/ingredient/excel

+
+ diff --git a/diet-web/src/main/resources/static/menu.html b/diet-web/src/main/resources/static/menu.html new file mode 100644 index 0000000..280ea02 --- /dev/null +++ b/diet-web/src/main/resources/static/menu.html @@ -0,0 +1,12 @@ +

ʳײ

+
+

ʳײֹܽ϶, ͲΪʳ׵ĻϢ(ɾIJ)ʳ׵鲿(ʳIJƷɾIJ)ʳ׵ͷ

+
+ + diff --git a/diet-web/src/main/resources/static/menu/dish.html b/diet-web/src/main/resources/static/menu/dish.html new file mode 100644 index 0000000..c81b53b --- /dev/null +++ b/diet-web/src/main/resources/static/menu/dish.html @@ -0,0 +1,582 @@ +

1. ѯʳײƷб

+
+

GET /api/menu/dish

+
+

:

+
menuId=1   // ʳױ
+
+

:

+
{
+  "body": [
+    {
+      "day": 1,
+      "dish": 1,
+      "id": 1,
+      "ingredient": [
+        {
+          "isMain": true,
+          "key": "011101",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        },
+        {
+          "isMain": false,
+          "key": "2101001",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        }
+      ],
+      "marks": "ʳ",
+      "meal": "",
+      "menu": 1,
+      "name": "ѳ",
+      "vender": 1
+    },
+    {
+      "day": 1,
+      "dish": 2,
+      "id": 3,
+      "ingredient": [
+        {
+          "isMain": true,
+          "key": "011101",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        }
+      ],
+      "marks": "ʳ",
+      "meal": "",
+      "menu": 1,
+      "name": "ѳ",
+      "vender": 1
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ѯմƷ(ʾ)

+
+

GET /api/menu/dish

+
+

:

+
{
+  "body": [
+    {
+      "day": 1,
+      "dish": 1,
+      "id": 1,
+      "ingredient": [
+        {
+          "isMain": true,
+          "key": "011101",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        },
+        {
+          "isMain": false,
+          "key": "2101001",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        }
+      ],
+      "marks": "ʳ",
+      "meal": "",
+      "menu": 1,
+      "name": "ѳ",
+      "vender": 1
+    },
+    {
+      "day": 1,
+      "dish": 2,
+      "id": 3,
+      "ingredient": [
+        {
+          "isMain": true,
+          "key": "011101",
+          "value": {
+            "": 500,
+            "": 300
+          }
+        }
+      ],
+      "marks": "ʳ",
+      "meal": "",
+      "menu": 1,
+      "name": "ѳ",
+      "vender": 1
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ʳӲƷ

+
+

PUT /api/menu/dish

+
+

:

+
menuId=1        // ʳױ
+dishId=1        // ƷID
+day=1           // һ
+meal=        // Ǹʹ
+mark=        // ִ֧ͲƷϲһı
+ingredient=[{"isMain":true,"key":"011101","value":{"":500,"":300}}]
+                // Ʒɷ, Map<Ⱥ, List<(ʳ,,Ƿ)>>
+
+

:

+
{
+  "body": 1, // ʳײƷı
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ޸ʳײƷ

+
+

POST /api/menu/dish

+
+

:

+
menuId=1        // ʳױ
+menuDishId=1    // ʳϵIJƷID
+mark=        // ִ֧ͲƷϲһı
+ingredient=[{"isMain":true,"key":"011101","value":{"":500,"":300}}]
+                // Ʒɷ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ɾʳϵIJƷ

+
+

DELETE /api/menu/dish

+
+

:

+
menuId=1        // ʳID
+menuDishId=1    // ʳϵIJƷ, ɾƷ, ʳϵвƷ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ʳӲƷ

+
+

PUT /api/menu/dish/batch

+
+

:

+
{
+    "menuIds" : [1,2,3],
+    "dishes" : [
+        {
+            "dish": 1,
+            "day" : 1,
+            "meal": "",
+            "mark": "ʳ",
+            "items" : [{
+                "key" : "011101",
+                "isMain": true,
+                "value" : {
+                    "": 13.56,
+                    "": 13.56
+                }
+            }]
+        }
+    ]
+}
+
+

:

+
{
+  "body": [
+     1,2,3
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. ʳ

+
+

GET /api/menu/dish/export

+
+

:

+
id=1 // ʳID
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

8. ʳ׷

+
+

GET /api/menu/dish/analysis

+
+

:

+
id=1      // ʳID, 
+day=3     // һ, Ĭϵ 
+crow=xxx  //Ⱥ,ĬϵһȺ
+
+

:

+
{
+  "body": {
+    "day": 5,
+    "crow": "10",
+    "meals": [
+      ""
+    ],
+    "types": {
+      "": 5,
+      "Ϻ": 2,
+      "ζƷ": 1
+    },
+    "ingredient": [
+      {
+        "nutrition": "ʳά/g",
+        "virtual": 0,
+        "standard": "1~2",
+        "ul": "-",
+        "overload": -1,
+        "conclusion": ""
+      },
+      {
+        "nutrition": "/mg",
+        "virtual": 2,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "άB1/mg",
+        "virtual": 1,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "̼ˮ/g",
+        "virtual": 0.1,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "/g",
+        "virtual": 0.1,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "άB2/mg",
+        "virtual": 2,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "֬/g",
+        "virtual": 0.0,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "/mg",
+        "virtual": 0.1,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "άA/gRAE",
+        "virtual": 4,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      },
+      {
+        "nutrition": "kcal/kcal",
+        "virtual": 0.79,
+        "standard": "-",
+        "ul": "-",
+        "overload": "-",
+        "conclusion": "-"
+      }
+    ]
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

9. ʳ׷

+
+

GET /api/menu/dish/analysis/energy

+
+

:

+
id=1      // ʳID, 
+day=3     // һ, Ĭϵ 
+crow=xxx  //Ⱥ,ĬϵһȺ
+
+

:

+
{
+  "body": {
+    "day": 5,
+    "crow": "10",
+    "meals": [
+      ""
+    ],
+    "energy": [
+      {
+        "name": "/",
+        "standard": "10~20",
+        "value": 10.0,
+        "conclusion": ""
+      },
+      {
+        "name": "֬/",
+        "standard": "20~30",
+        "value": 0.0,
+        "conclusion": "Ե"
+      },
+      {
+        "name": "̼ˮ/",
+        "standard": "50~60",
+        "value": 10.0,
+        "conclusion": "Ե"
+      }
+    ]
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

10. ʳ׷

+
+

GET /api/menu/dish/analysis/types

+
+

:

+
id=1      // ʳID, 
+crow=xxx  //Ⱥ,ĬϵһȺ
+
+

:

+
{
+  "body": {
+    "dayRule": [
+      [
+        {
+          "day": 1,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 1,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 1,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 2,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 2,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 2,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 3,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 3,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 3,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 4,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 4,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 4,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 5,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 5,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 5,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 6,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 6,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 6,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ],
+      [
+        {
+          "day": 7,
+          "name": "Ϻ",
+          "standard": 0,
+          "supplied": 2,
+          "lack": 0
+        },
+        {
+          "day": 7,
+          "name": "",
+          "standard": 0,
+          "supplied": 5,
+          "lack": 0
+        },
+        {
+          "day": 7,
+          "name": "ζƷ",
+          "standard": 0,
+          "supplied": 1,
+          "lack": 0
+        }
+      ]
+    ],
+    "weekRule": [
+      {
+        "name": "",
+        "standard": 0,
+        "supplied": 35,
+        "lack": 0
+      },
+      {
+        "name": "Ϻ",
+        "standard": 0,
+        "supplied": 14,
+        "lack": 0
+      },
+      {
+        "name": "ζƷ",
+        "standard": 0,
+        "supplied": 7,
+        "lack": 0
+      }
+    ]
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/menu/menu.html b/diet-web/src/main/resources/static/menu/menu.html new file mode 100644 index 0000000..c8a61a4 --- /dev/null +++ b/diet-web/src/main/resources/static/menu/menu.html @@ -0,0 +1,172 @@ +

1. ѯʳ(IDȡϢ)

+
+

GET /api/menu

+
+

:

+
id=1
+
+

:

+
{
+  "body": {
+    "created": 1694014254000,
+    "crows": [
+      "",
+      ""
+    ],
+    "day": 1,
+    "id": 1,
+    "meals": [
+      "",
+      "",
+      ""
+    ],
+    "modify": 1695404897000,
+    "month": [
+      1,
+      2,
+      3,
+      4,
+      5,
+      6,
+      7,
+      8,
+      9
+    ],
+    "name": "2343ʳ",
+    "nutrient": 1,
+    "operate": "system",
+    "scale": {
+      "": 0,
+      "": 0
+    },
+    "status": "ݸ",
+    "vender": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ѯʳб

+
+

GET /api/menu

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+name=Ѽ   // Ʋ
+vender=1       // ݵλ
+status=1       // ״̬
+startTime=2023-03-01 // ʱβ
+endTime=2024-03-01
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "created": 1694014254000,
+        "crows": [
+          "",
+          ""
+        ],
+        "day": 1,
+        "id": 1,
+        "meals": [
+          "",
+          "",
+          ""
+        ],
+        "modify": 1695404897000,
+        "month": [
+          1,
+          2,
+          3,
+          4,
+          5,
+          6,
+          7,
+          8,
+          9
+        ],
+        "name": "2343ʳ",
+        "nutrient": 1,
+        "operate": "system",
+        "scale": {
+          "": 0,
+          "": 0
+        },
+        "status": "ݸ",
+        "vender": 1
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ʳ

+
+

PUT /api/menu

+
+

:

+
vendors=1,2,3  // Чҵ˲
+name=Ѽ   // 
+nutrient=1      // Ӫƻ
+day=7           // 
+meals=,, // ʹ
+month=1,2,3,4,5,6,7,8,9,10,11,12  // ·
+crows=,  //Ⱥ  
+
+

:

+
{
+  "body": [
+      1,
+      2,
+      3
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ޸ʳ

+
+

POST /api/menu

+
+

:

+
id=1 // ʳID
+name=Ѽ   // 
+nutrient=1      // Ӫƻ
+day=7           // 
+meals=,, // ʹ
+month=1,2,3,4,5,6,7,8,9,10,11,12  // ·
+crows=,  //Ⱥ  
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ɾʳ

+
+

DELETE /api/menu

+
+

:

+
id=1 // ʳID
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/menu/release.html b/diet-web/src/main/resources/static/menu/release.html new file mode 100644 index 0000000..63e9cfb --- /dev/null +++ b/diet-web/src/main/resources/static/menu/release.html @@ -0,0 +1,94 @@ +

1. ѯʳ׷б

+
+

GET /api/menu/release

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+name=Ѽ   // Ʋ
+vender=1       // ݵλ
+startTime=2023-03-01 // ʱβ
+endTime=2024-03-01
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "created": 1694014254000,
+        "crows": [
+          "",
+          ""
+        ],
+        "day": 1,
+        "id": 1,
+        "meals": [
+          "",
+          "",
+          ""
+        ],
+        "modify": 1695404897000,
+        "month": [
+          1,
+          2,
+          3,
+          4,
+          5,
+          6,
+          7,
+          8,
+          9
+        ],
+        "name": "2343ʳ",
+        "nutrient": 1,
+        "operate": "system",
+        "scale": {
+          "": 0,
+          "": 0
+        },
+        "status": "",
+        "vender": 1
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2.

+
+

PUT /api/menu/release

+
+

:

+
id=1                            // ʳID
+scale={"":10, "":20}  // Ⱥֲ
+startTime=2023-03-01 // ʱ
+endTime=2024-03-01
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ȡ

+
+

DELETE /api/menu/release

+
+

:

+
id=1 // ʳID
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/menu/report.html b/diet-web/src/main/resources/static/menu/report.html new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/diet-web/src/main/resources/static/menu/report.html @@ -0,0 +1 @@ + diff --git a/diet-web/src/main/resources/static/menu/review.html b/diet-web/src/main/resources/static/menu/review.html new file mode 100644 index 0000000..8ab0353 --- /dev/null +++ b/diet-web/src/main/resources/static/menu/review.html @@ -0,0 +1,129 @@ +

1. ѯʳб

+
+

GET /api/menu/review

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+name=Ѽ   // Ʋ
+status=3       // ״̬ѯ
+vender=1       // ݵλ
+startTime=2023-03-01 // ʱβ
+endTime=2024-03-01
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "created": 1694014254000,
+        "crows": [
+          "",
+          ""
+        ],
+        "day": 1,
+        "id": 1,
+        "meals": [
+          "",
+          "",
+          ""
+        ],
+        "modify": 1695404897000,
+        "month": [
+          1,
+          2,
+          3,
+          4,
+          5,
+          6,
+          7,
+          8,
+          9
+        ],
+        "name": "2343ʳ",
+        "nutrient": 1,
+        "operate": "system",
+        "scale": {
+          "": 0,
+          "": 0
+        },
+        "status": "",
+        "vender": 1
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ͨͨ

+
+

POST /api/menu/review

+
+

:

+
id=1                 // ʳID
+pass=true // true-ͨfalse-˲ͨ
+reason=OK // 
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ύ

+
+

PUT /api/menu/review

+
+

:

+
id=1 // ʳID
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3.

+
+

DELETE /api/menu/review

+
+

:

+
id=1 // ʳID
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ״̬ͳ

+
+

GET /api/menu/review/count

+
+

:

+
{
+  "body": [
+    {
+      "count": 2,
+      "status": 2
+    },
+    {
+      "count": 2,
+      "status": 1
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/nutrition.html b/diet-web/src/main/resources/static/nutrition.html new file mode 100644 index 0000000..f0016de --- /dev/null +++ b/diet-web/src/main/resources/static/nutrition.html @@ -0,0 +1,268 @@ +

Ӫƻ

+

1. ѯƻ(ģҳѯ ڹ˵Ĺҳ)

+
+

GET /api/nutrition

+
+

:

+
pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+keyword=   // ѯؼ
+
+

:

+
{
+  "body": {
+    "content": [
+      {
+        "foodCategoryDay": {
+          "ˮ": 20,
+          "߲": 50,
+          "": 10,
+          "": 30
+        },
+        "foodCategoryWeek": {
+          "ˮ": 100,
+          "߲": 200,
+          "": 500,
+          "": 300
+        },
+        "id": 1,
+        "ingredient": {
+          "": {
+            "vitamin-a": {
+              "min": 2,
+              "max": 10,
+              "ul": 5
+            }
+          },
+          "": {
+            "vitamin-a": {
+              "min": 2,
+              "max": 10,
+              "ul": 5
+            }
+          }
+        },
+        "name": "Ͳָ",
+        "overflow": 0.51,
+        "vendors": [
+          1
+        ]
+      }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. ѯƻ(IDȷѯ)

+
+

GET /api/nutrition/select

+
+

:

+
id=1           
+
+

:

+
{
+  "body": {
+    "foodCategoryDay": {
+      "ˮ": 20,
+      "߲": 50,
+      "": 10,
+      "": 30
+    },
+    "foodCategoryWeek": {
+      "ˮ": 100,
+      "߲": 200,
+      "": 500,
+      "": 300
+    },
+    "id": 1,
+    "ingredient": {
+      "": {
+        "vitamin-a": {
+          "min": 2,
+          "max": 10,
+          "ul": 5
+        }
+      },
+      "": {
+        "vitamin-a": {
+          "min": 2,
+          "max": 10,
+          "ul": 5
+        }
+      }
+    },
+    "name": "Ͳָ",
+    "overflow": 0.51,
+    "vendors": [
+      1
+    ]
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ѯƻ(ݵλģѯ)

+
+

GET /api/nutrition/select

+
+

:

+
vender=1   // DZ
+keyword=  
+
+

:

+
{
+  "body": [
+      {
+        "foodCategoryDay": {
+          "ˮ": 20,
+          "߲": 50,
+          "": 10,
+          "": 30
+        },
+        "foodCategoryWeek": {
+          "ˮ": 100,
+          "߲": 200,
+          "": 500,
+          "": 300
+        },
+        "id": 1,
+        "ingredient": {
+          "": {
+            "vitamin-a": {
+              "min": 2,
+              "max": 10,
+              "ul": 5
+            }
+          },
+          "": {
+            "vitamin-a": {
+              "min": 2,
+              "max": 10,
+              "ul": 5
+            }
+          }
+        },
+        "name": "Ͳָ",
+        "overflow": 0.51,
+        "vendors": [
+          1
+        ]
+      }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. Ӽƻ(˽ӿ)

+
+

PUT /api/nutrition

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+name=Ͳָ   //   
+vendors=1,2.3      //   λб
+overflow=0.5       //   
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ޸ļƻ(˽ӿ)

+
+

POST /api/nutrition

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+id=1   // 
+name=Ͳָ   //  
+vendors=1,2,3      //  λб
+overflow=0.5       //  
+foodCategoryDay={"ˮ": 20, "߲": 50, "": 10, "": 30}
+foodCategoryWeek={"ˮ": 200, "߲": 500, "": 100, "": 300}
+ingredient={"": {"vitamin-a": {"ul": 5, "max": 10, "min": 2}}, "": {"vitamin-a": {"ul": 5, "max": 10, "min": 2}}}
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ɾƻ(˽ӿ)

+
+

DELETE /api/nutrition

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+id=1  // 
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ʳĴ(ҵ˽ӿ)

+
+

PUT /api/ingredient/mark

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+nutrient=010101
+mark=        // , ȡֵ:  /
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ȡ(ҵ˽ӿ)

+
+

DELETE /api/ingredient/mark

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+nutrient=010101
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. (˽ӿ)

+
+

PUT /api/ingredient/mark

+
+

:

+
Content-Type: multipart/form-data
+files   // ش
+
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/user.html b/diet-web/src/main/resources/static/user.html new file mode 100644 index 0000000..8a231a3 --- /dev/null +++ b/diet-web/src/main/resources/static/user.html @@ -0,0 +1,269 @@ +

û

+

1. UIDǷظ

+
+

GET /api/user/check?uid=zzz

+
+

:

+
{
+  "body": false, // trueʶuidδռ
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. û

+
+

PUT /api/user

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+uid=ccc   // , ûID, ظ
+name=   // , û
+password=BE56E057F20F883E // , MD5ܺдȡ16λʾԭΪ123456
+roleId=2 //ɫ,ֻԼλĽɫӽɫбѡһ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ɾû

+
+

DELETE /api/user

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+uid=ccc   // 
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ޸û

+
+

POST /api/user

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+uid=ccc   // ûID, 
+name=   // ޸
+password=BE56E057F20F883E // ޸
+roleId=2 //޸Ľɫ, 0-ʶսɫ-ʶɫ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ȡûб

+
+

GET /api/user

+
+

:

+
{
+  "body": [
+    {
+      "name": "ҵ˲˺",
+      "phone": "13919103409",
+      "roleId": 2,
+      "roleName": "Ա",
+      "uid": "xxx",
+      "time" 123412341234
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ȡǰûڶ˵Ȩб

+
+

GET /api/role/item

+
+

:

+
{
+  "body": [
+    {
+      "category": "Ȩ",
+      "id": 18,
+      "itemName": "ʹ",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ȩ",
+      "id": 19,
+      "itemName": "ݴ-ʾ",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ȩ",
+      "id": 20,
+      "itemName": "ݴ-ʾ(LED)",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "",
+      "id": 21,
+      "itemName": "-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "",
+      "id": 22,
+      "itemName": "-༭",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ʳĹ",
+      "id": 23,
+      "itemName": "ʳб-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ʳĹ",
+      "id": 24,
+      "itemName": "ʳ-/",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ʒ",
+      "id": 25,
+      "itemName": "Ʒб-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ʒ",
+      "id": 26,
+      "itemName": "Ʒ-/༭/ɾ",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ʳ׹",
+      "id": 27,
+      "itemName": "ʳб-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ʳ׹",
+      "id": 28,
+      "itemName": "ʳ-/༭/ɾ",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ʳ׹",
+      "id": 29,
+      "itemName": "ʳ˼¼-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ϣ",
+      "id": 30,
+      "itemName": "λϢ-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "Ϣ",
+      "id": 31,
+      "itemName": "λϢ-޸",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ϵͳ",
+      "id": 32,
+      "itemName": "ûб-鿴",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ϵͳ",
+      "id": 33,
+      "itemName": "û-/༭/ɾ",
+      "itemType": "ҵ"
+    },
+    {
+      "category": "ϵͳ",
+      "id": 34,
+      "itemName": "ɫȨ-鿴//༭/ɾ",
+      "itemType": "ҵ"
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. ӽɫ

+
+

PUT /api/role

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+roleName=ccc    // , ɫ
+items=1,2,3   // Ȩ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

8. ɾɫ

+
+

DELETE /api/role

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+roleId=1   // 
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

9. ޸Ľɫ

+
+

POST /api/role

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+roleId=1        // 
+roleName=ccc    // ɫ
+items=1,2,3     // Ȩ
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

10. ȡɫб

+
+

GET /api/role

+
+

:

+
{
+  "body": [
+    {
+      "id": 2,
+      "roleItems": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34],
+      "roleName": "Ա",
+      "roleType": "ϵͳ",
+      "vender": 1
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/diet-web/src/main/resources/static/vender.html b/diet-web/src/main/resources/static/vender.html new file mode 100644 index 0000000..00236c5 --- /dev/null +++ b/diet-web/src/main/resources/static/vender.html @@ -0,0 +1,191 @@ +

λ

+

1. ˺ظ

+
+

GET /api/vender/check/account?account=xxx

+
+

:

+
{
+  "body": false, // trueδռ,
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

2. 鵥λظ

+
+

GET /api/vender/check/name?name=xxx

+
+

:

+
{
+  "body": false, // trueδռ,
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

3. ѯλ

+
+

GET /api/vender/config

+
+

:

+
{
+  "body": {
+    "breakfast": 10.00,
+    "dinner": 10.00,
+    "lunch": 10.00
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

4. ޸ĵλ

+
+

POST /api/vender/config

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+vender=1    // 
+breakfast=10.00 // 
+dinner=10 // 
+lunch=10 // 
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

5. ҵ

+
+

PUT /api/vender

+

˽ӿ

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+account=ccc   // , ʼԱ˺, ظ
+password=BE56E057F20F883E // , MD5ܺдȡ16λʾԭΪ123456
+name=   // , λ
+category=ѧУ  // , λ
+expire=2019-10-10 // , ʱ 
+icon=23423 //λlogo, ǰ˿õbase64ַ
+address=
+contacts=
+phone=
+email=
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

6. ɾҵ

+
+

DELETE /api/vender

+

˽ӿ

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+vender=1    // 
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

7. ޸ҵ

+
+

POST /api/vender

+

ѺͿؽˣ˻ҵ˺Ų

+
+

:

+
Content-Type:application/x-www-form-urlencoded
+venderId=1    // 
+expire=2019-10-10 // ˿ԸĹʱ 
+status=false // ˿Ը״̬, false-رգtrue-
+category=ѧУ  // λ
+account=ccc   // İ󶨵˻, ԶΪ˻ԱȨ, ظ
+name=       // ĵλ
+icon=23423 //λlogo, ǰ˿õbase64ַ, ͼʮKBɣ̫
+address=
+contacts=
+phone=
+email=
+
+

:

+
{
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

8. ȡҵб

+
+

GET /api/vender

+

˽ӿ

+
+

:

+
keyword=1    // ݵλģƥ
+pageSize=20  // Ĭ20, ȫDZ
+pageNo=0     // Ĭ0, 0ʼ
+
+

:

+
{
+  "body": {
+    "content": [
+        {
+          "account": "xxx",
+          "address": "·",
+          "area": "",
+          "category": "Сѧ",
+          "city": "ɶ",
+          "contacts": "",
+          "expire": 1695033585000,
+          "icon": "data:image/jpeg;base64,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",
+          "id": 1,
+          "name": "ɶʵСѧ",
+          "phone": "13919103408",
+          "province": "Ĵʡ",
+          "status": true
+        }
+    ],
+    "number": 0,
+    "size": 20,
+    "totalElements": 1,
+    "totalPages": 1
+  },
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+

9. ȡҵб(ѡҵʹ)

+
+

GET /api/vender/select

+

˽ӿ

+
+

:

+
keyword=1    // ݵλģƥ
+vendors=1,2,3  // IDȡλϢ
+
+

:

+
{
+  "body": [
+    {
+      "account": "xxx",
+      "category": "Сѧ",
+      "id": 1,
+      "name": "ɶʵСѧ"
+    }
+  ],
+  "code": 200,
+  "desc": "ɹ",
+  "success": true
+}
+
+ diff --git a/doc/basic.md b/doc/basic.md new file mode 100644 index 0000000..d00e678 --- /dev/null +++ b/doc/basic.md @@ -0,0 +1,372 @@ +# 基础部分 + +# 1. 登录 + +### GET /user/login?uid=xxx&pwd=BE56E057F20F883E +> +> MD5加密后大写取后16位,示例原密码为123456 + +### 输出: +```json +{ + "body": { + "roleName": "超级管理员", + "uid": "xxx", + "admin": false, // 是否管理端,true-是管理端, false-业务端 + "name": "业务端测试账号", + "phone": "13919103409", + "roleId": 2, + "roleItems": [ + { + "category": "基础权限", + "id": 18, + "itemName": "使用流程", + "itemType": "业务端" + } + ], + "vender": { + "account": "13919103408", + "address": "百仁路", + "contacts": "曹先生", + "expire": 1693651185000, + "icon": "data:image/jpeg;base64,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", + "id": 1, // 单位编号,后续操作都需要这个单位编号 + "name": "成都实验小学", + "phone": "13919103408", + "status": true + } + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 2. 登出 + +### GET /user/logout + +### 输出: +```json +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + + + +# 3. 修改自己的密码 + +### POST /api/password +> +> Content-Type:application/x-www-form-urlencoded + +### 输入: +```texttext +oldPassword=BE56E057F20F883E // 原密码 +password=BE56E057F20F883E // 新密码 +``` + +### 输出: +```json +{ +"code": 200, +"desc": "成功", +"success": true +} +``` + +# 4. 获取所有枚举信息 + +### GET /api/enum + +### 输出: +```json +{ + "body": { + "nutrient": [ + { + "key": "fiber", + "measurement": "g", + "nrv": 25.00, + "value": "膳食纤维" + }, + { + "key": "calcium", + "measurement": "mg", + "nrv": 800.00, + "value": "钙" + }, + { + "key": "vb1", + "measurement": "mg", + "nrv": 1.40, + "value": "维生素B1" + }, + { + "key": "carbs", + "measurement": "g", + "nrv": 300.00, + "value": "碳水化合物" + }, + { + "key": "vb2", + "measurement": "mg", + "nrv": 1.40, + "value": "维生素B2" + }, + { + "key": "va", + "measurement": "μgRAE", + "nrv": 800.00, + "value": "维生素A" + }, + { + "key": "vc", + "measurement": "mg", + "nrv": 100.00, + "value": "维生素C" + }, + { + "key": "protein", + "measurement": "g", + "nrv": 60.00, + "value": "蛋白质" + }, + { + "key": "fat", + "measurement": "g", + "nrv": 60.00, + "value": "脂肪" + }, + { + "key": "iron", + "measurement": "mg", + "nrv": 15.00, + "value": "铁" + }, + { + "key": "zinc", + "measurement": "mg", + "nrv": 15.00, + "value": "锌" + }, + { + "key": "energy", + "measurement": "kcal", + "nrv": 2000.00, + "value": "能量kcal" + } + ], + "menuStatus": [ + { + "key": 0, + "value": "草稿" + }, + { + "key": 1, + "value": "提交审核" + }, + { + "key": 2, + "value": "审核通过" + }, + { + "key": 3, + "value": "审核失败" + }, + { + "key": 4, + "value": "禁用" + }, + { + "key": 5, + "value": "发布" + } + ], + "markType": [ + { + "key": "常用", + "value": "常用" + }, + { + "key": "忌用", + "value": "忌用" + } + ], + "mealType": [ + { + "key": "早餐", + "value": "早餐" + }, + { + "key": "午餐", + "value": "午餐" + }, + { + "key": "晚餐", + "value": "晚餐" + } + ], + "category": [ + { + "key": "蛋类", + "value": "蛋类" + }, + { + "key": "大豆类及其制品", + "value": "大豆类及其制品" + }, + { + "key": "婴幼儿食品", + "value": "婴幼儿食品" + }, + { + "key": "鱼虾类", + "value": "鱼虾类" + }, + { + "key": "畜肉类", + "value": "畜肉类" + }, + { + "key": "坚果", + "value": "坚果" + }, + { + "key": "含酒精饮料", + "value": "含酒精饮料" + }, + { + "key": "调味品", + "value": "调味品" + }, + { + "key": "糖类", + "value": "糖类" + }, + { + "key": "其他", + "value": "其他" + }, + { + "key": "蔬菜类", + "value": "蔬菜类" + }, + { + "key": "水果类", + "value": "水果类" + }, + { + "key": "菌藻类", + "value": "菌藻类" + }, + { + "key": "小吃、甜饼", + "value": "小吃、甜饼" + }, + { + "key": "谷类", + "value": "谷类" + }, + { + "key": "禽肉类", + "value": "禽肉类" + }, + { + "key": "奶及奶制品", + "value": "奶及奶制品" + }, + { + "key": "烹调油", + "value": "烹调油" + }, + { + "key": "速食食品", + "value": "速食食品" + }, + { + "key": "薯类", + "value": "薯类" + }, + { + "key": "饮料类", + "value": "饮料类" + } + ], + "mark": [ + { + "key": "主荤", + "value": "主荤" + }, + { + "key": "水果", + "value": "水果" + }, + { + "key": "辅食", + "value": "辅食" + }, + { + "key": "糕点", + "value": "糕点" + }, + { + "key": "次荤", + "value": "次荤" + }, + { + "key": "主食", + "value": "主食" + }, + { + "key": "面食", + "value": "面食" + }, + { + "key": "素菜", + "value": "素菜" + }, + { + "key": "奶", + "value": "奶" + }, + { + "key": "粥类", + "value": "粥类" + }, + { + "key": "汤类", + "value": "汤类" + }, + { + "key": "饮料类", + "value": "饮料类" + } + ], + "venderType": [ + { + "key": "学校", + "value": "学校" + }, + { + "key": "医院", + "value": "医院" + }, + { + "key": "事业单位", + "value": "事业单位" + }, + { + "key": "其他", + "value": "其他" + } + ] + }, + "code": 200, + "desc": "成功", + "success": true +} +``` \ No newline at end of file diff --git a/doc/change.md b/doc/change.md new file mode 100644 index 0000000..19dcdd5 --- /dev/null +++ b/doc/change.md @@ -0,0 +1,27 @@ +# 修改记录 + +### 9.10 +* 基础协议 +* 用户协议 +* 角色协议 +* 单位协议 + +### 9.11 +* 用户列表协议返回字段增加添加时间:time +* 添加角色协议请求中的权限项列表允许为空 +* 角色列表协议返回的权限项由字符串给位数组 + + +### 9.17 +* 食材接口: mark接口的(PUT, DELETE) ingredient(DELETE)参数名字改了下, +* 单位接口: vender接口的(GET) 增加keyword参数,并改为分页查询; + 增加/api/vender/select(GET), 用于管理端其他部分下来狂选择单位 +* 单位接口: 添加和修改增加category字段,代表单位类型, 查询接口select支持批量查询单位信息 +* 枚举接口: 添加单位类型枚举取值范围 +* 食材接口: 增加select接口用于批量查询, 增加下载导入模板接口, 增加数据导入接口 +* 菜品接口: 完善营养标签接口 + + +### 9.23 +* 菜品接口: 更改食材列表数据结构,增加是否主料(boolean)。 +* 枚举接口: 增加餐次枚举和食谱状态枚举 diff --git a/doc/dish.md b/doc/dish.md new file mode 100644 index 0000000..a72fcc4 --- /dev/null +++ b/doc/dish.md @@ -0,0 +1,260 @@ +# 菜品部分 + +# 1. 查询菜品(根据名字模糊查询) + +> GET /api/dish + +### 输入: +```text +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +keyword=番茄鸡蛋汤 // 查询关键字 +mark=汤类 // 标签 取值参照GET /api/basic/enum 接口中的 mark +``` + +### 输出: +```json +{ + "body": { + "content": [ + { + "icon": "data:image/png;base64,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", + "id": 2, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": 500 + } + ], + "marks": "主食", + "modify": 1695277128000, + "month": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 12 + ], + "name": "番茄炒鸡蛋", + "vender": 4 + } + ], + "number": 0, + "size": 20, + "totalElements": 2, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 2. 查询菜品(根据ID精确查询) +> GET /api/dish + +### 输入: +```text +id=1 +``` + +### 输出: +```json +{ + "icon": "data:image/png;base64,iVBORkJggg==", + "id": 1, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": 500 + } + ], + "marks": "汤类", + "month": [ + 1, + 3, + 5, + 7, + 9, + 12 + ], + "name": "番茄鸡蛋汤", + "vender": 1 +} +``` + +# 3. 根据名称模糊查询(用于食谱部分选择菜品) + +> GET /api/dish/select + +### 输入: +```text +keyword=婆 +``` + +### 输出: +~~~json +{ + "body": [ + { + "id": 25, + "ingredient": [ + { + "key": "3355", + "value": 22 + }, + { + "key": "3378", + "value": 111 + } + ], + "marks": "面食", + "name": "麻婆豆腐" + }, + { + "id": 24, + "ingredient": [ + { + "isMain": false, + "key": "3355", + "value": 22 + }, + { + "isMain": true, + "key": "3378", + "value": 111 + } + ], + "marks": "面食", + "name": "麻婆豆腐" + } + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 4. 营养标签 +> GET /api/dish/label + +### 输入: +```text +ids=1,2,3 // 传了ID返回指定标签,不传返回所有菜品的标签列表 +``` + +### 输出: +```json +{ + "body": [ + { + "component": [ + { + "name": "钙", + "nutrition": "60.00(mg)", + "nvr": "0.00%" + }, + { + "name": "vitamin-a", + "nutrition": "115.00(-)", + "nvr": "-" + }, + { + "name": "蛋白质", + "nutrition": "75.00(g)", + "nvr": "1.00%" + }, + { + "name": "脂肪", + "nutrition": "50.00(g)", + "nvr": "1.00%" + }, + { + "name": "能量kcal", + "nutrition": "50.00(kcal)", + "nvr": "0.00%" + } + ], + "ingredients": [ + "小麦" + ], + "name": "番茄炒鸡蛋" + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 5. 添加菜品 +> PUT /api/dish + +### 输入: +```text +Content-Type:application/x-www-form-urlencoded +name=番茄炒鸡蛋 // 必填 名称 +vendors=1,2,3 // 单位列表, 管理端必填,业务端没用 +icon= // 图片 +month=1,2,3 //月份 +mark=汤类 // 必填 标签 取值参照GET /api/basic/enum 接口中的 mark +ingredient=[{"key": "011101", "value": 500, "isMain": true}] // 食材列表 +``` + +### 输出: +```json +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 6. 修改 +> POST /api/dish + +### 输入: +```text +Content-Type:application/x-www-form-urlencoded +id=1 // 必填 +name=番茄炒鸡蛋 // 必填 名称 +vendors=1,2,3 // 单位列表 +icon= // 图片 +month=1,2,3 // 月份 +mark=汤类 // 标签 取值参照GET /api/basic/enum 接口中的 mark +ingredient=[{"key": "011101", "value": 500, "isMain": true}] // 食材列表 +``` + +### 输出: +```json +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 7. 删除 +> DELETE /api/dish + +### 输入: +Content-Type:application/x-www-form-urlencoded +ids=9,10 // 必填 + +输出: +```text +{ + "code": 200, + "desc": "成功", + "success": true +} +``` \ No newline at end of file diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..df0da6d --- /dev/null +++ b/doc/index.md @@ -0,0 +1,45 @@ +# 协议约定 + +> 协议格式: restfull + json + utf-8 +> +> 协议格式中,凡是用 * 标识字段均为必须字段,否则为可选字段。 +> +> 密码:协议中涉及password字段全部使用16位的MD5加密传输(MD5加密后取后16位,大写) + +### 协议列表 +* [修改记录](change.md) +* [基础协议](basic.md) +* [用户权限](user.md) +* [单位协议](vender.md) +* [食材协议](ingredient.md) +* [营养计划](nutrition.md) +* [菜品协议](dish.md) +* [食谱协议](menu.md) + * [食谱基础协议](menu/menu.md) + * [食谱菜品协议](menu/dish.md) + * [食谱审批协议](menu/review.md) + * [食谱发布协议](menu/release.md) + * [食谱分析协议](menu/report.md) +### 响应示例 + +```json +{ + "body": {}, + "code": 1, + "desc": "成功" +} +``` + +### 返回码表 + +```text +基础返回码: + success (200, "成功"), + + invalid_user_password (300, "用户名或者密码错误!"), + + expired_vender (301, "账户过期,请联系管理员续费!"), + + illegal_argument (400, "参数错误!"), + need_login (401, "未登录!"), + not_support_operate (404, "不支持的� \ No newline at end of file diff --git a/doc/ingredient.md b/doc/ingredient.md new file mode 100644 index 0000000..18b0d2d --- /dev/null +++ b/doc/ingredient.md @@ -0,0 +1,211 @@ + +# 食材部分 + +# 1. 查询食材 + +> GET /api/ingredient + +### 输入: +``` +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +keyword=01 // 查询关键字,模糊匹配用 +type=谷薯类 // 食材类型 +mark=常用 // 食材标记. 业务端用标记,管理端没用 +``` + +### 输出: +``` +{ + "body": { + "content": [ + { + "key": "011101", + "mark": "常用", + "name": "小麦", + "nutrient": { + "fat": 10, + "energy": 10, + "calcium": 12, + "protein": 15, + "vitamin-a": 23 + }, + "time": 1693759354000, + "type": "谷薯类" + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 2. 查询食材(根据ID批量查询) + +> GET /api/ingredient/select + +### 输入: +``` +keys=011101,011102,011103 // 根据ID检索, 两个查询条件2选一 +keyword=01 // 查询关键字,模糊匹配用 +``` +### 输出: +``` +{ + "body": [ + { + "key": "011101", + "name": "小麦", + "type": "谷薯类" + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 3. 添加食材(管理端接口) + +> PUT /api/ingredient + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +key=010101 // 必填 +name=测试食材 // 必填 +type=粗粮 // 必填 全部必填 取值范围见(/api/basic/enum) category +nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23} +// 必填 取值范围见(/api/basic/enum) nutrient +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 4. 修改食材(管理端接口) + +> POST /api/ingredient + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +key=010101 // 必填 +name=测试食材 +type=粗粮 +nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23} + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` +# 5. 删除食材(管理端接口) + +> DELETE /api/ingredient + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +keys=010101,0101012,0101013 // 必填 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 6. 食材打标(业务端接口) + +> PUT /api/ingredient/mark + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +key=010101 // 食材编号 +mark=常用 // 必填, 取值: 常用/忌用 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 7. 取消打标(业务端接口) + +> DELETE /api/ingredient/mark + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +key=010101 // 食材编号 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 8. 批量导入(管理端接口) + +> PUT http://localhost:9527/api/ingredient/excel + +### 输入: +``` +Content-Type: multipart/form-data; boundary=boundary + +--boundary +Content-Disposition: form-data; name="file"; filename="a.xlsx" + +< C:\Users\CCC\Documents\WeChat Files\wxid_40aqnb839lkd12\FileStorage\File\2023-09\谷物及制品.xlsx + +--boundary +Content-Disposition: form-data; name="extraInfo"; +``` + +### 输出: +``` +Content-Disposition: attachment;filename*=utf-8''%5B%E5%AF%BC%E5%85%A5%E7%BB%93%E6%9E%9C%5Da.xlsx +Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8 +Transfer-Encoding: chunked +Date: Sun, 17 Sep 2023 18:49:10 GMT +Keep-Alive: timeout=60 +Connection: keep-alive + +Response file saved. +> [导入结果]a-2.xlsx +``` + +# 9.下载导入模板 + +> GET /api/ingredient/excel \ No newline at end of file diff --git a/doc/menu.md b/doc/menu.md new file mode 100644 index 0000000..44ea9f7 --- /dev/null +++ b/doc/menu.md @@ -0,0 +1,9 @@ + +# 食谱部分 +> 食谱部分功能较多, 按照类型拆分为食谱的基础信息部分(包括增删改查)、食谱的详情部分(食谱下面的菜品增删改查)、食谱的审批和发布、分析及部分 + + * [食谱基础协议](menu/menu.md) + * [食谱菜品协议](menu/dish.md) + * [食谱审批协议](menu/review.md) + * [食谱发布协议](menu/release.md) + * [食谱分析协议](menu/report.md) \ No newline at end of file diff --git a/doc/menu/dish.md b/doc/menu/dish.md new file mode 100644 index 0000000..0926275 --- /dev/null +++ b/doc/menu/dish.md @@ -0,0 +1,625 @@ +# 1. 查询食谱菜品列表 + +> GET /api/menu/dish + +### 输入: +```text +menuId=1 // 食谱编号 +``` + +### 输出: +~~~json +{ + "body": [ + { + "day": 1, + "dish": 1, + "id": 1, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": { + "轻体力": 500, + "重体力": 300 + } + }, + { + "isMain": false, + "key": "2101001", + "value": { + "轻体力": 500, + "重体力": 300 + } + } + ], + "marks": "主食", + "meal": "早餐", + "menu": 1, + "name": "番茄炒蛋", + "vender": 1 + }, + { + "day": 1, + "dish": 2, + "id": 3, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": { + "轻体力": 500, + "重体力": 300 + } + } + ], + "marks": "主食", + "meal": "早餐", + "menu": 1, + "name": "番茄炒鸡蛋", + "vender": 1 + } + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 2. 查询今日带量菜品(大屏显示) + +> GET /api/menu/dish + +### 输出: +~~~json +{ + "body": [ + { + "day": 1, + "dish": 1, + "id": 1, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": { + "轻体力": 500, + "重体力": 300 + } + }, + { + "isMain": false, + "key": "2101001", + "value": { + "轻体力": 500, + "重体力": 300 + } + } + ], + "marks": "主食", + "meal": "早餐", + "menu": 1, + "name": "番茄炒蛋", + "vender": 1 + }, + { + "day": 1, + "dish": 2, + "id": 3, + "ingredient": [ + { + "isMain": true, + "key": "011101", + "value": { + "轻体力": 500, + "重体力": 300 + } + } + ], + "marks": "主食", + "meal": "早餐", + "menu": 1, + "name": "番茄炒鸡蛋", + "vender": 1 + } + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 3. 向食谱添加菜品 + + +> PUT /api/menu/dish + +### 输入: +```text +menuId=1 // 食谱编号 +dishId=1 // 菜品ID +day=1 // 属于那一天 +meal=早餐 // 属于那个餐次 +mark=打标 // 支持打和菜品上不一样的标 +ingredient=[{"isMain":true,"key":"011101","value":{"轻体力":500,"重体力":300}}] + // 菜品成分, Map<人群, List<(食材,用量,是否主料)>> +``` + +### 输出: +~~~json +{ + "body": 1, // 食谱菜品的编号 + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 4. 修改食谱菜品 + + +> POST /api/menu/dish + +### 输入: +```text +menuId=1 // 食谱编号 +menuDishId=1 // 食谱上的菜品ID +mark=打标 // 支持打和菜品上不一样的标 +ingredient=[{"isMain":true,"key":"011101","value":{"轻体力":500,"重体力":300}}] + // 菜品成分 +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 5. 删除食谱上的菜品 + + +> DELETE /api/menu/dish + +### 输入: +```text +menuId=1 // 食谱ID +menuDishId=1 // 食谱上的菜品编号, 传了删这个菜品, 不传则清空这个食谱上的所有菜品 +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 6. 批量向食谱添加菜品 + + +> PUT /api/menu/dish/batch + +### 输入: +```json +{ + "menuIds" : [1,2,3], + "dishes" : [ + { + "dish": 1, + "day" : 1, + "meal": "早餐", + "mark": "主食", + "items" : [{ + "key" : "011101", + "isMain": true, + "value" : { + "重体力": 13.56, + "轻体力": 13.56 + } + }] + } + ] +} +``` + +### 输出: +~~~json +{ + "body": [ + 1,2,3 + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 7. 导出食谱 + + +> GET /api/menu/dish/export + +### 输入: +```text +id=1 // 食谱ID +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 8. 食谱分析 + + +> GET /api/menu/dish/analysis + +### 输入: +```text +id=1 // 食谱ID, 必填 +day=3 // 那一天, 默认当天 +crow=xxx //人群,默认第一个人群 +``` + +### 输出: +~~~json +{ + "body": { + "day": 5, + "crow": "10", + "meals": [ + "早餐" + ], + "types": { + "蛋类": 5, + "鱼虾类": 2, + "调味品": 1 + }, + "ingredient": [ + { + "nutrition": "膳食纤维/g", + "virtual": 0, + "standard": "1~2", + "ul": "-", + "overload": -1, + "conclusion": "不足" + }, + { + "nutrition": "钙/mg", + "virtual": 2, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "维生素B1/mg", + "virtual": 1, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "碳水化合物/g", + "virtual": 0.1, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "蛋白质/g", + "virtual": 0.1, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "维生素B2/mg", + "virtual": 2, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "脂肪/g", + "virtual": 0.0, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "铁/mg", + "virtual": 0.1, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "维生素A/μgRAE", + "virtual": 4, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + }, + { + "nutrition": "能量kcal/kcal", + "virtual": 0.79, + "standard": "-", + "ul": "-", + "overload": "-", + "conclusion": "-" + } + ] + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + + +# 9. 食谱分析 + +> GET /api/menu/dish/analysis/energy + +### 输入: +```text +id=1 // 食谱ID, 必填 +day=3 // 那一天, 默认当天 +crow=xxx //人群,默认第一个人群 +``` + +### 输出: +~~~json +{ + "body": { + "day": 5, + "crow": "10", + "meals": [ + "早餐" + ], + "energy": [ + { + "name": "蛋白质/总能量", + "standard": "10~20", + "value": 10.0, + "conclusion": "合适" + }, + { + "name": "脂肪/总能量", + "standard": "20~30", + "value": 0.0, + "conclusion": "略低" + }, + { + "name": "碳水化合物/总能量", + "standard": "50~60", + "value": 10.0, + "conclusion": "略低" + } + ] + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 10. 食谱分析 + +> GET /api/menu/dish/analysis/types + +### 输入: +```text +id=1 // 食谱ID, 必填 +crow=xxx //人群,默认第一个人群 +``` + +### 输出: +~~~json +{ + "body": { + "dayRule": [ + [ + { + "day": 1, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 1, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 1, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 2, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 2, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 2, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 3, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 3, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 3, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 4, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 4, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 4, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 5, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 5, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 5, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 6, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 6, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 6, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ], + [ + { + "day": 7, + "name": "鱼虾类", + "standard": 0, + "supplied": 2, + "lack": 0 + }, + { + "day": 7, + "name": "蛋类", + "standard": 0, + "supplied": 5, + "lack": 0 + }, + { + "day": 7, + "name": "调味品", + "standard": 0, + "supplied": 1, + "lack": 0 + } + ] + ], + "weekRule": [ + { + "name": "蛋类", + "standard": 0, + "supplied": 35, + "lack": 0 + }, + { + "name": "鱼虾类", + "standard": 0, + "supplied": 14, + "lack": 0 + }, + { + "name": "调味品", + "standard": 0, + "supplied": 7, + "lack": 0 + } + ] + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ \ No newline at end of file diff --git a/doc/menu/menu.md b/doc/menu/menu.md new file mode 100644 index 0000000..8cd50fa --- /dev/null +++ b/doc/menu/menu.md @@ -0,0 +1,194 @@ +# 1. 查询食谱(根据ID获取基础信息) + +> GET /api/menu + +### 输入: +```text +id=1 +``` + +### 输出: +~~~json +{ + "body": { + "created": 1694014254000, + "crows": [ + "轻体力", + "重体力" + ], + "day": 1, + "id": 1, + "meals": [ + "早餐", + "午餐", + "晚餐" + ], + "modify": 1695404897000, + "month": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "name": "23年度43周食谱", + "nutrient": 1, + "operate": "system", + "scale": { + "轻体力": 0, + "重体力": 0 + }, + "status": "草稿", + "vender": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 2. 查询食谱列表 + + +> GET /api/menu + +### 输入: +```text +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +name=番茄鸡蛋汤 // 根据名称查 +vender=1 // 根据单位查 +status=1 // 根据状态查 +startTime=2023-03-01 // 根据时间段查 +endTime=2024-03-01 +``` + +### 输出: +~~~json +{ + "body": { + "content": [ + { + "created": 1694014254000, + "crows": [ + "轻体力", + "重体力" + ], + "day": 1, + "id": 1, + "meals": [ + "早餐", + "午餐", + "晚餐" + ], + "modify": 1695404897000, + "month": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "name": "23年度43周食谱", + "nutrient": 1, + "operate": "system", + "scale": { + "轻体力": 0, + "重体力": 0 + }, + "status": "草稿", + "vender": 1 + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 3. 创建新食谱 + + +> PUT /api/menu + +### 输入: +```text +vendors=1,2,3 // 管理端有效,业务端不用 +name=番茄鸡蛋汤 // 名称 +nutrient=1 // 营养计划编号 +day=7 // 天数 +meals=早餐,午餐,晚餐 // 餐次 +month=1,2,3,4,5,6,7,8,9,10,11,12 // 适用月份 +crows=重体力,轻体力 //人群 +``` + +### 输出: +~~~json +{ + "body": [ + 1, + 2, + 3 + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 4. 修改食谱 + + +> POST /api/menu + +### 输入: +```text +id=1 // 食谱ID +name=番茄鸡蛋汤 // 名称 +nutrient=1 // 营养计划编号 +day=7 // 天数 +meals=早餐,午餐,晚餐 // 餐次 +month=1,2,3,4,5,6,7,8,9,10,11,12 // 适用月份 +crows=重体力,轻体力 //人群 +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 5. 删除食谱 + + +> DELETE /api/menu + +### 输入: +```text +id=1 // 食谱ID +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ \ No newline at end of file diff --git a/doc/menu/release.md b/doc/menu/release.md new file mode 100644 index 0000000..ed2f9b0 --- /dev/null +++ b/doc/menu/release.md @@ -0,0 +1,108 @@ +# 1. 查询食谱发布列表 + + +> GET /api/menu/release + +### 输入: +```text +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +name=番茄鸡蛋汤 // 根据名称查 +vender=1 // 根据单位查 +startTime=2023-03-01 // 根据时间段查 +endTime=2024-03-01 +``` + +### 输出: +~~~json +{ + "body": { + "content": [ + { + "created": 1694014254000, + "crows": [ + "轻体力", + "重体力" + ], + "day": 1, + "id": 1, + "meals": [ + "早餐", + "午餐", + "晚餐" + ], + "modify": 1695404897000, + "month": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "name": "23年度43周食谱", + "nutrient": 1, + "operate": "system", + "scale": { + "轻体力": 0, + "重体力": 0 + }, + "status": "发布", + "vender": 1 + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 2. 发布 + + +> PUT /api/menu/release + +### 输入: +```text +id=1 // 食谱ID +scale={"重体力":10, "轻体力":20} // 人群分布 +startTime=2023-03-01 // 发布时间段 +endTime=2024-03-01 +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + + +# 3. 取消发布 + + +> DELETE /api/menu/release + +### 输入: +```text +id=1 // 食谱ID +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ \ No newline at end of file diff --git a/doc/menu/report.md b/doc/menu/report.md new file mode 100644 index 0000000..e69de29 diff --git a/doc/menu/review.md b/doc/menu/review.md new file mode 100644 index 0000000..e191a2d --- /dev/null +++ b/doc/menu/review.md @@ -0,0 +1,149 @@ +# 1. 查询食谱审核列表 + + +> GET /api/menu/review + +### 输入: +```text +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +name=番茄鸡蛋汤 // 根据名称查 +status=3 // 根据状态查询 +vender=1 // 根据单位查 +startTime=2023-03-01 // 根据时间段查 +endTime=2024-03-01 +``` + +### 输出: +~~~json +{ + "body": { + "content": [ + { + "created": 1694014254000, + "crows": [ + "轻体力", + "重体力" + ], + "day": 1, + "id": 1, + "meals": [ + "早餐", + "午餐", + "晚餐" + ], + "modify": 1695404897000, + "month": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + "name": "23年度43周食谱", + "nutrient": 1, + "operate": "system", + "scale": { + "轻体力": 0, + "重体力": 0 + }, + "status": "审核中", + "vender": 1 + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 2. 审核通过、不通过 + + +> POST /api/menu/review + +### 输入: +```text +id=1 // 食谱ID +pass=true // true-审核通过,false-审核不通过 +reason=OK // 审批意见 +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + + +# 3. 提交审核 + + +> PUT /api/menu/review + +### 输入: +```text +id=1 // 食谱ID +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 3. 禁用 + + +> DELETE /api/menu/review + +### 输入: +```text +id=1 // 食谱ID +``` + +### 输出: +~~~json +{ + "code": 200, + "desc": "成功", + "success": true +} +~~~ + +# 4. 根据状态统计数量 +>GET /api/menu/review/count + +### 输出: +~~~json +{ + "body": [ + { + "count": 2, + "status": 2 + }, + { + "count": 2, + "status": 1 + } + ], + "code": 200, + "desc": "成功", + "success": true +} +~~~ \ No newline at end of file diff --git a/doc/nutrition.md b/doc/nutrition.md new file mode 100644 index 0000000..9d128b6 --- /dev/null +++ b/doc/nutrition.md @@ -0,0 +1,301 @@ + +# 营养计划部分 + +# 1. 查询计划(根据名字模糊分页查询, 用于管理端的管理页面) + +> GET /api/nutrition + +### 输入: +``` +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +keyword=青少年 // 查询关键字 +``` + +### 输出: +``` +{ + "body": { + "content": [ + { + "foodCategoryDay": { + "水果类": 20, + "蔬菜类": 50, + "谷薯类": 10, + "畜肉禽类": 30 + }, + "foodCategoryWeek": { + "水果类": 100, + "蔬菜类": 200, + "谷薯类": 500, + "畜肉禽类": 300 + }, + "id": 1, + "ingredient": { + "轻体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + }, + "中体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + } + }, + "name": "青少年就餐指导", + "overflow": 0.51, + "vendors": [ + 1 + ] + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 2. 查询计划(根据ID精确查询) +> GET /api/nutrition/select + +### 输入: +``` +id=1 +``` + +### 输出: +``` +{ + "body": { + "foodCategoryDay": { + "水果类": 20, + "蔬菜类": 50, + "谷薯类": 10, + "畜肉禽类": 30 + }, + "foodCategoryWeek": { + "水果类": 100, + "蔬菜类": 200, + "谷薯类": 500, + "畜肉禽类": 300 + }, + "id": 1, + "ingredient": { + "轻体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + }, + "中体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + } + }, + "name": "青少年就餐指导", + "overflow": 0.51, + "vendors": [ + 1 + ] + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 3. 查询计划(根据单位和名称模糊查询) +> GET /api/nutrition/select + +### 输入: +``` +vender=1 // 非必填 +keyword=青 +``` + +### 输出: +``` +{ + "body": [ + { + "foodCategoryDay": { + "水果类": 20, + "蔬菜类": 50, + "谷薯类": 10, + "畜肉禽类": 30 + }, + "foodCategoryWeek": { + "水果类": 100, + "蔬菜类": 200, + "谷薯类": 500, + "畜肉禽类": 300 + }, + "id": 1, + "ingredient": { + "轻体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + }, + "中体力": { + "vitamin-a": { + "min": 2, + "max": 10, + "ul": 5 + } + } + }, + "name": "青少年就餐指导", + "overflow": 0.51, + "vendors": [ + 1 + ] + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 4. 添加计划(管理端接口) + +> PUT /api/nutrition + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +name=青少年就餐指导 // 必填 名称 +vendors=1,2.3 // 必填 单位列表 +overflow=0.5 // 必填 溢出 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 5. 修改计划(管理端接口) + +> POST /api/nutrition + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +id=1 // 必填 +name=青少年就餐指导 // 名称 +vendors=1,2,3 // 单位列表 +overflow=0.5 // 溢出 +foodCategoryDay={"水果类": 20, "蔬菜类": 50, "谷薯类": 10, "畜肉禽类": 30} +foodCategoryWeek={"水果类": 200, "蔬菜类": 500, "谷薯类": 100, "畜肉禽类": 300} +ingredient={"中体力": {"vitamin-a": {"ul": 5, "max": 10, "min": 2}}, "轻体力": {"vitamin-a": {"ul": 5, "max": 10, "min": 2}}} +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` +# 6. 删除计划(管理端接口) + +> DELETE /api/nutrition + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +id=1 // 必填 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 5. 食材打标(业务端接口) + +> PUT /api/ingredient/mark + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +nutrient=010101 +mark=常用 // 必填, 取值: 常用/忌用 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 6. 取消打标(业务端接口) + +> DELETE /api/ingredient/mark + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +nutrient=010101 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 7. 批量导入(管理端接口) + +> PUT /api/ingredient/mark + +### 输入: +``` +Content-Type: multipart/form-data +files // 必传 + +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` \ No newline at end of file diff --git a/doc/user.md b/doc/user.md new file mode 100644 index 0000000..0d8fc9d --- /dev/null +++ b/doc/user.md @@ -0,0 +1,304 @@ +# 用户部分 + +# 1. 检查UID的是否重复 + +> GET /api/user/check?uid=zzz + +### 输出: +``` +{ + "body": false, // true标识uid未被占用 + "code": 200, + "desc": "成功", + "success": true +} +``` + + +# 2. 添加用户 + +> PUT /api/user + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +uid=ccc // 必填, 用户ID, 不能重复 +name=曹 // 必填, 用户姓名 +password=BE56E057F20F883E // 必填, MD5加密后大写取后16位,示例原密码为123456 +roleId=2 //角色编号,只能是自己单位的角色,必填,从角色列表选择一个 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 3. 删除用户 + +> DELETE /api/user + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +uid=ccc // 必填, +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 4. 修改用户 + +> POST /api/user + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +uid=ccc // 用户ID, 必填 +name=曹 // 修改姓名 +password=BE56E057F20F883E // 修改密码 +roleId=2 //修改角色, 0-标识回收角色,其他-标识分配角色 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + + +# 5. 获取用户列表 + +> GET /api/user + +### 输出: +``` +{ + "body": [ + { + "name": "业务端测试账号", + "phone": "13919103409", + "roleId": 2, + "roleName": "超级管理员", + "uid": "xxx", + "time" 123412341234 + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` + + +# 6. 获取当前用户所在端的权限项列表 + +> GET /api/role/item + +### 输出: +``` +{ + "body": [ + { + "category": "基础权限", + "id": 18, + "itemName": "使用流程", + "itemType": "业务端" + }, + { + "category": "基础权限", + "id": 19, + "itemName": "数据大屏-大屏显示", + "itemType": "业务端" + }, + { + "category": "基础权限", + "id": 20, + "itemName": "数据大屏-大屏显示(LED)", + "itemType": "业务端" + }, + { + "category": "配餐设置", + "id": 21, + "itemName": "配餐设置-查看", + "itemType": "业务端" + }, + { + "category": "配餐设置", + "id": 22, + "itemName": "配餐设置-编辑", + "itemType": "业务端" + }, + { + "category": "食材管理", + "id": 23, + "itemName": "食材列表-查看", + "itemType": "业务端" + }, + { + "category": "食材管理", + "id": 24, + "itemName": "食材-常用/忌用", + "itemType": "业务端" + }, + { + "category": "菜品管理", + "id": 25, + "itemName": "菜品列表-查看", + "itemType": "业务端" + }, + { + "category": "菜品管理", + "id": 26, + "itemName": "菜品-新增/编辑/删除", + "itemType": "业务端" + }, + { + "category": "食谱管理", + "id": 27, + "itemName": "食谱列表-查看", + "itemType": "业务端" + }, + { + "category": "食谱管理", + "id": 28, + "itemName": "食谱-新增/编辑/删除", + "itemType": "业务端" + }, + { + "category": "食谱管理", + "id": 29, + "itemName": "食谱审核记录-查看", + "itemType": "业务端" + }, + { + "category": "基础信息管理", + "id": 30, + "itemName": "单位基础信息-查看", + "itemType": "业务端" + }, + { + "category": "基础信息管理", + "id": 31, + "itemName": "单位基础信息-修改", + "itemType": "业务端" + }, + { + "category": "系统设置", + "id": 32, + "itemName": "用户列表-查看", + "itemType": "业务端" + }, + { + "category": "系统设置", + "id": 33, + "itemName": "用户-新增/编辑/删除", + "itemType": "业务端" + }, + { + "category": "系统设置", + "id": 34, + "itemName": "角色权限-查看/新增/编辑/删除", + "itemType": "业务端" + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 7. 添加角色 + +> PUT /api/role + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +roleName=ccc // 必填, 角色名称 +items=1,2,3 // 赋予的权限项 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 8. 删除角色 + +> DELETE /api/role + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +roleId=1 // 必填 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 9. 修改角色 + +> POST /api/role + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +roleId=1 // 必填 +roleName=ccc // 角色名称 +items=1,2,3 // 赋予的权限项 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + + +# 10. 获取角色列表 + +> GET /api/role + +### 输出: +``` +{ + "body": [ + { + "id": 2, + "roleItems": [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34], + "roleName": "超级管理员", + "roleType": "系统", + "vender": 1 + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` \ No newline at end of file diff --git a/doc/vender.md b/doc/vender.md new file mode 100644 index 0000000..6dbb895 --- /dev/null +++ b/doc/vender.md @@ -0,0 +1,221 @@ +# 单位部分 + +# 1. 检验账号重复性 + +> GET /api/vender/check/account?account=xxx + +### 输出: +``` +{ + "body": false, // true未被占用,可用 + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 2. 检查单位名称重复性 + +> GET /api/vender/check/name?name=xxx + +### 输出: +``` +{ + "body": false, // true未被占用,可用 + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 3. 查询单位配置 + +> GET /api/vender/config + +### 输出: +``` +{ + "body": { + "breakfast": 10.00, + "dinner": 10.00, + "lunch": 10.00 + }, + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 4. 修改单位配置 + +> POST /api/vender/config + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +vender=1 // 必填 +breakfast=10.00 // 必填 +dinner=10 // 必填 +lunch=10 // 必填 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 5. 添加企业 + +> PUT /api/vender +> +> 管理端接口 +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +account=ccc // 必填, 初始管理员账号, 不能重复 +password=BE56E057F20F883E // 必填, MD5加密后大写取后16位,示例原密码为123456 +name=曹 // 必填, 单位名称 +category=学校 // 必填, 单位类型 +expire=2019-10-10 // 必填, 过期时间 +icon=23423 //单位logo, 前端可用的base64字符串 +address= +contacts= +phone= +email= +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 6. 删除企业 + +> DELETE /api/vender +> +> 管理端接口 +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +vender=1 // 必填 +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + +# 7. 修改企业 + +> POST /api/vender +> +> 续费和开关仅管理端,其他管理端或者业务端主账号操作 + +### 输入: +``` +Content-Type:application/x-www-form-urlencoded +venderId=1 // 必填 +expire=2019-10-10 // 仅管理端可以改过期时间 +status=false // 仅管理端可以改状态, false-关闭,true-打开 +category=学校 // 单位类型 +account=ccc // 改绑定的主账户, 将自动为改账户赋管理员权限, 不能重复 +name=曹 // 改单位名称 +icon=23423 //单位logo, 前端可用的base64字符串, 最大好像就几十KB吧,不能太大 +address= +contacts= +phone= +email= +``` + +### 输出: +``` +{ + "code": 200, + "desc": "成功", + "success": true +} +``` + + +# 8. 获取企业列表 + +> GET /api/vender +> +> 管理端接口 +### 输入: + +``` +keyword=1 // 根据单位名称模糊匹配 +pageSize=20 // 默认20, 全部非必填 +pageNo=0 // 默认0, 从0开始 +``` +### 输出: +``` +{ + "body": { + "content": [ + { + "account": "xxx", + "address": "百仁路", + "area": "青羊区", + "category": "小学", + "city": "成都市", + "contacts": "曹先生", + "expire": 1695033585000, + "icon": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAkACQAAD/4QBaRXhpZgAATU0AKgAAAAgABQMBAAUAAAABAAAASgMDAAEAAAABAAAAAFEQAAEAAAABAQAAAFERAAQAAAABAAAWJVESAAQAAAABAAAWJQAAAAAAAYagAACxj//bAEMAAgEBAgEBAgICAgICAgIDBQMDAwMDBgQEAwUHBgcHBwYHBwgJCwkICAoIBwcKDQoKCwwMDAwHCQ4PDQwOCwwMDP/bAEMBAgICAwMDBgMDBgwIBwgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDP/AABEIAIAAgAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APxL/Z4/Z2ufjTqEl1dSy2Og2UgjnnQfvJ3wCYo88bsEEschQwJByAfqzwf8KPDfgKzSHSdF0+12f8tTEJJ2PvI2XP0zgdgKd8L/AArb+CPh3oul2yqI7W0j3MB/rJGG6R/+BOzH2zjtW9XxeOx9StNpP3eiP6u4P4OweU4SEpQUqzScpNXab6R7Jbab7vyKKKK88+2CiiigAooooAKKKKACiiigAoIyKKKAOZ8a/Bvwv8QbOSLVNFsZJJBgXMUQhuUPYiRQG4znByueoNfJ3x++Ad78FNcjKyNe6LfE/ZLsrhgRyYpB2ceo4YcjB3Kv2vXJ/HHwZD49+E+uafMgaRbZ7q2OASk0Sl0I9MkFSf7rt616WX4+dGok3eL3X6o+D404NwmaYOdWnBRrxTcZJWba15Zd09lfZ6rS6fRaJ/yBbP8A694//QRVqquif8gWz/694/8A0EVarzpbn3FL4F6BRRRSNAqbTNMutb1KCysba4vby6bZDb28TSSynrhVUEngE8dhWh4P8H3HjLUJo0mt7GysYTdahf3GfI0+AEAyPjJYksqqigs7sqqCWFfR3wp+A1vaeH7m61y18XeF/CNlJYPqWjWemS/8Jb4qs7x4ktZpGABSC4aX9xDDm3ZoGjlkjmntZrjanRczzsdmVPDL3t/89vm7qy81dpO54rpnwGvvtCQ6ldtHeSSPEum6PZSa1qBeMxiRCsP7iN08xdySTpIuV3INyk9r4b/ZPvNa8Qf2Svgf4mXF+9ze2sX2i6sNH3tZ2/2q5BSdWCmK3Ikb5yACOTkZ6W//AGjF+BlnY2Nrqml+Fb3TLGDTzpvhrT7O7ubSZLG5066leYgWlvJfQXAkuof9NcXMSyF4HiSKOxpVh8ZvjDbw654e+Evx48TwrK7QailxrFzFHvtYbUiL7FbwRR7rSC2hIThoool5VVFbxp09t3/Xr+R41bG41rnk4xjbRt216W1jdbbSd9bdzirn9la41DxVZ6LZ+H/H1lqWoLpSwQqtlqxim1ORY7CG4KPAtu8zsqhZCCpdNwGQK4W9+DWqTWUl5oc9n4q0+OSWIy6YJPOVosGQGCRUlOxXjZmjV41EifOdwJ9x1v4n/FT9mC2tbvxZ4S+OPwzs3vbW7judTluHs5Ly22/ZZBBqlqyO8QiUxjzQUMMbLho1Knw7+I9l4g+EOmeDLCSy8QeAfDejG2s9M07fpGpaVePftO+t6jJtubtYYlmaWR9NM8RFlZrNFGqohHTpt22f9f1sVDHYyEfa6SjdLR3Vra66q97KK5uurPmBHWRQykMp6Ed6Wvov9oP9nuWHS7DXrqSKTR9cEK6P41lnj26kXVvI/tlUUR2sl2sbzRTBpBEGeCeWaa1umt/nzVtJu/D+rXWn39rcWN9YzNb3NtOhSW3kUlWRlPIYEEEVzVKbg7M9vBY6niYc8P6/r7+6RXooorM7Qqrrf/IEvP8Ar3k/9BNWqq63/wAgW8/695P/AEE1Ud0Z1fgfoGif8gWz/wCveP8A9BFWqq6J/wAgWz/694//AEEVapS3Cl8C9AoJwOAzHsAMk0V1PwilfRfE1x4gTdu8J2UurwsAPkuUAW1fkj7lw8L98+WeCMiiKu7BVqckHL+vJfM9t+Dfhe8+F/hrWNSuvBdh4u8AeG71PD/i1WuZLXUL3VZtqzf2ZPxDJe2ausEdsxk85J7nbbzxT3Txu+HHgbxp8fvjHoPwN8A6pZweKL63k0a+vLnXCtnpNso33Wm2TtIR5KlGkufs4Ju5xJsH2eNM9H8Z72b4P/ArwneXF/odxrXgvQoLbzRaTafr0d08MSWMd3DNGJo0tC1zLFvkkSRtPhmjS3JYN9S/AT4CeGv+CWv7NnwB8Rat4f0lv2ivjF4t0/T9Nv7i38248M6ZeXFv9rUI7NH5kdm3kF1UOst9gfLuJ9WnR5ny9Fq/TsvXZH57js09jSdZLmq1G401rZyUW+aSb+GK96TSTs0re6kWvi54r/Z4/wCCBv2Pwn4V8Ct8WvjreWK38usa9HHGum2826MEzbCII28tttvboWZV/eyDcrt4Lff8Fy/2wPjnql5ceEWt7SG2KedbeFPBf9oRWu4HbuMy3Lru2k/M3JBxgDA9q/b+8beLtD/bC+IWsXn7Dek+LvDej3+y71+XQHv28QxLGu28a9W0k2q0Pl/LGcw7SrNvVgPKf2Ffi9+0D8X9f+KWtfsu3Hw3+C/gu81TTbi78M3d/aXMNpcSwRWkbQPc27uwlaLczFUUvJhQxzXRVnJVPZU5OK10imnp11tfz1PEy/C4eeB/tDG0YVqrjFyqVqkZRvJr3bR5+Xf3Uo26N3HeFP8Agrl+2p4d3N4o8GX3jvRWKLcWXiH4bzR20gLqFG62ihw24qFJyNxHDHArsrb4J/Bz/gq34qjj8DaLdfspftRaQsmp/wBhXEEtppviF0G8ywbViZZUbDGWKOOZVd2eKdVV07z9sf8AbT+Pya18O/hX4B+Kng/xR408HeHYtd+KGvRx6X/YlrqTalapahp3hVIRBcFFCBUJSSNpFPzkN/a1+KXxa+FX7CnxEm/aJ+Jvwv174naF4g0m5+F0vhma3OuaJq8Fxm4l2xQxhNkZGQVz5fnpJlXVTWmsajckt7peujvdPp6nNGT/AHVXBUqeHq1HaPs5Su05KKcqbgoyg78zUrPktJNPQ+OfAviO907WfE3gXx54PVde0W6ubLxP4am0+e6/sq6kUwNrlhp9tJGlxOgfEtvGyI4mS4hkjhmvCPLfjl8NprOzb/RNahudD0201OwGrJCuqXfhy4CCye8SFmVLm3WS3RgSGaCe3cKIkU19u/8ABSDWrP8Aad/ZL+DP7anhfQdHtdbs2Gg/ESyt3MD6ixcWhiPzMDGJFngVjmXyryEkkRgL8uw+CPCfw48AapfTaeNX1SPxK9nc63dasq3Fxok6Rxx3S2zztI8VxFqkBnby48F7dFckzsnHWp29zdbp+T2/ryZ9blGYc8ViVHlldxlDflqRdpLV6LtvdSjpdXPmqirWvaHceF9ev9Lu0kjutNuZLSZZF2urxsVYEdjkdKq15Z94pJq6Cqut/wDIFvP+veT/ANBNWqq63/yBbz/r3k/9BNVHdEVfgfoGif8AIFs/+veP/wBBFWqq6J/yBbP/AK94/wD0EVapS3Cl8C9Ar0L4H+Dbjx3pPiHTbHTdS1jUb2fSbW2s9OjEl5cmS/jDJEpBBcqDjcNoxlvlBrz2vQP2fPEUeha1rCzTajDC1gbmRrEt9oCQsGkZAksLMyRGSUIJYw/lbGZVZiLpfFqc+P5vYScd1Z/c0z1T9tqz1LXfH3iDSdUuLz7Xq3izSrQ3eqaRbaZLDGtjOYt9rZyTxRoDfzMfLZzJguVDOVH1z/wXGl8R/APxL+yb4i1+50vVvE3w5tojq1jYSyCwlureWzmEkQZFKJcG1lA4yoi287Mn4M+IM2n634P8WaXoereMNXOjpo3iFdS8RaclhqWo+Ws9tLOIBPOY49t/aug82XMdv5hIBwPuz/grb8Odc/4KLfscfsx/HLQmsbvUvE0Fr4Q8QC0R1t7PUdQkigVlU5KQx36XEB3OSGliAzya9SEnKnV5d9H9zPgcZCNHHZe67Sppzg21bWVJWVtLKSurdGkrbnrXh/8Aao8H/tU/tVeFPHGjftj698PfC/iy+02/k+FusWVu08dxGbUQ28csjNFDHO/ksV2vlmmKOwJMfxp+11+yP4H1v/gov+0FrXivUofA/wAI/h74o0mbX7i0g33A/ta6tkeO1TY+HKyXlxwjgCHaEO4bffda+JX7Lvw6/bQuPBetfAeTX/inpHjKSTVvEtx4ja30tbpdTVba5UJKwAZGhke3EIWNz5R8wK0te4fs432ueA/+CjH7b3/COaLpvirxPe3/AIfj0fTr65S0s726ksLi5SKSSR8qoVGLFA7AIzKhA2jrlBVrRnZvm6XfRvZ+a2R8th8VLLHOvhozhH2SspezjePtacbqUU7+7J2nUTdmn3OZ/Yn+AP7HOmfsz/tCeKvhx4o8T+LvBNx4SutJ8TBTNLe2eli1M0rJDNAm26VhKylVIASI4yct8J/tC6R+wvY/CDXh8J9Y+M1x48S2jbRhqEEa2Msxlw0cu6NSFCAsxGOGG0swKj9cEu/jZ8TP2YPi3pfj74I/DXT9WvfAuow6dJ4c8QjUIdbv/JmRNOlh8uOSIh9q5WZwGDYcHBr88fj/AA/tO+HP2L9b0Pxp+yx8M9G8P6X4aisNU8XpoFr/AGzbwQhEa8aSO4OJsrvZkjwpLOFUDhYqmlTSUVs/sv8ApFcNY+pPG1J1K8rucFZ4inqrJa6fvO1kou2m5m/sii4+Iv8AwQG/aa8PySBYfDHia01m3Misyqq/2dcMigEYJMDHPQF8kGvH/B/iq48Pfs2/ELVtPtNDtNQt/BuiOdSuvDcWoT2pkhk08bbp3Bs2ligWOJkSYmR/uxD98PdrXTr79jr/AIN39RN7Y3cev/tHeK4v7PtxEfO+xOqMj7fvMstrp7shAIP2qLHDZPzlrfiOPw7b654B1Tw74gvbbXmsvCem65Y3VrbxefZWsFpcrE01pPvha63uzwSxGWJlVmIxjjqe6oJ78v53sfWYGPtp4qVNJwddtbaqPs1PfR3cX9zPHvjbB9n+L/iMb2kaS9aZmJyS0gWRsn13MRXL1ufEzWLfxB8SfEF9aLJHaXepXEturncyxmRtgJ9lwKw68uXxM+/w6apRT3svyCqut/8AIFvP+veT/wBBNWqq63/yBbz/AK95P/QTRHdFVfgfoGif8gWz/wCveP8A9BFWqq6J/wAgWz/694//AEEVapS3Cl8C9ArS8IeJG8H+KLHVPssd9HZy5ntJGKx3sDApNbsRyFliZ42I5Ac4wcGs2iltqVKKknF7M+kvF3x28QeMbzwn4bbRNU+KHiWzu5I11Z7TzNU1zwp/ZsMFjpULQL+7jFv9vkm3RyH7QVcsxgOfX/8Agm/+2FpHwA8U2PwM+J19pPiT9m34mXn9rWGtX7XFubByySWd3FJE/wDou27t4xMgI+z3G+TzVCF5Pn79i/8Aaab4K/EbQF1SZZNIsb+F2huLkw2t7aGYPPp9w25VWGQlpIZHOy2uf3hxHLcE9B4f0/4kftjftEeMPDt54Nk8UtqeoPe6lYeVBoE2jTFRGl6ksrNHa3DxxoHE0kiXAUCRpCsU0fo0qzupxd5X2t+D73Pi8wy6nKnUwmIio0lG/NzWad3aUb2UXFu61SW2qat9Ff8ABT3/AIJ/+Pvgf/wUbh+K2n+GdQ174YeIPFen+IItZsU+2R6fJJdRTXEd4sMYMC/aXk2OVKNG8eZGk8wDP8N/tj/B3wV/wUB+OGm+JPF3iOH4e/ES8029Tx74D1O5F5o+u2tqVuru1nizM1tLNdalFujVwUZdqFGynM/swftn/tIf8E+ry38K+C7668Z+EtHlkupvAev6Q1vq9gkqjO6zkAvoY+jq9q81oGbcTmRlPc6x/wAFbf2dfixDLqnxi/ZB0G78cEE3N5ptvahb6YZB8x5VimUcLw3mEc8nHPT7Snfmi+V3vaSuut7NdNfU+e+qY/2ccPXp+3hGHIpUpxjK3NGUZShOzUlyp6Nx12aPcfhr+1P+yj8H/hz45+G/gD9oL4gLa/FLw/qdjJreq/2vq93pOoTArFLBE1ujrIzXNzL+6KmSQMWIcq1SeIP+CbafAX4Gal8Wfjd+0N8cvi18LtIsotR1PwjJJd6UdatmljAhuoL+8DNGSwLW7eXI+NgyxCN8AaL+063w1/4KIXnxY/Zq8BXelafDcST6D4cvdIa8jt45rMQXMbQWz/LGZHmZVjkGwFBkAYrr/jT8VvjZ+314h874qa3qvjpPD7PexeB/CstvbWOnFSN32qeMm2tSgkAZne4vIlZg6wj5qpYuEotSjdq6Vr29d9fSxjU4bxNCtCdGu4U5qMqjm4Sq3/lVoJxaVlzc6S36av8A26f+CgmqftefFtfiZe29noXgH4eXF1pHwu0XyVja5ud67bx0bIJiRLeeXH7uNo7W3UEu8h4fxr+1bB4/+Anhm1TwzYaVbfDG3h07w7fglbx72Wy8qWAFTiWFJGlvS8gZ1eO3UkNdTPJU+CHx6j1Lwp4juJ7W303VFtZ4bbV7Oyigs/BNkIESx+yuwZ4ozPJcrLb5le9E3IN35d0njPjjxbD4hmtLPTYJrPQdHRodPglx5z7iDJcTFeDPMw3NgkKAkakrGprhqYiXx3u5b/1/WyPs8vymjDlw0aXLGl8Ouys7rzbu7t6vmk9FZPCRBGiqvCqMAelLRRXCfVBVXW/+QLef9e8n/oJq1VXW/wDkC3n/AF7yf+gmqjujOr8D9A0T/kC2f/XvH/6CKtVV0T/kC2f/AF7x/wDoIq1SluFL4F6BRQTgUE4leM8SRna6n7yn0I7GkaBXonwb/aO1r4S69pN5HNeefoamHTtQtJVi1LTIGGHt0d1ZJ7Vlyr2lwrwupZV8ouXrzeW7ihJ3yRrtG45bGB606KZZ4wyMrqehU5BqozcXeJjXw9OtDkqq6f8AX9eWh7R4h8Uv8d/jfp/jDxF4i0XxsoWCGewvLeHR7tYI4xGkUdu0sFsjgtuDQ3H+sZ5TuJbd6H+1PrXiHxTq9jc/CfwH8WtC0RUkSVg2q6lpqvHI8EcdusyOoDQxQ3LurktJdyKQNmT8qHDFlPXAJBHY9PzqFNCtLq7WNbK3muJiFRFhDSOT0AGMn6CtvbuzVt/vPOeUw54TUtIKyVvdt5pNR06aaH2p+0R4U8NX/wCzlHpcdt8SLbxO9s1xAdWs9RsdPuo2eM/6Rd6ld29urKBNuRLV8q8aqykfL578OP2r9R+APwFm8By6xoutWv2mSWGCwi/tKe1Vt4aFJbhWsYY8tNhhDdEG6lYKSwMfzXZWFnH+8t4bVd2G3Rooz75FSQX0NyzLHNHI0edwRgxXGSc49MH8jTliXfmirPYyo5DBUvYV5Ocebm1XX/L+rnReL/H934tt7ezWG10vR7OQy2umWYYW8UhGDKxZmeWYgkGWVmcg7QQuFGHRTVlV2ZQwLL1HpXO23ue1CEYLliOoprSKrqu4bpDhV7sfQDvTg24A9mGQfUetIoKq63/yBbz/AK95P/QTVqqut/8AIFvP+veT/wBBNVHdGdX4H6Bon/IFs/8Ar3j/APQRVqquif8AIFs/+veP/wBBFWS4BpS3Cl8C9D6k/YD074T6P4X1B/inffDuT/hNPEGlaZpi6heW/wDaugW1u1y9/e5ks7uK1R1kt40F0II53AJniSBmra/4Ko65pPxn1nw/8QPDfjj4b654VvTfkafpF/aprVjqN7q19fXkT2CpFMY4fOiT7RINk5H2jeGvNp+dPBXj/wAP+H9O02O/8M2mpNbxanBfrLFHIuoi4gKW8nmHEkLwSYI8plOFBV0Ytu1tO8U/C2fUriK48H6ta2dxDZxQ3H9pTzz2bpIhupWAlVZfNXzAqhVEYdBhmRpJe6NZOj7LT8fX8z5Oplc4Zm8ytOUtdFytW+HRNpp8qT0ve7uuZ2X6E/seftR/s3+EPgF4N8Jw6x4Ni1Gw8T213A3iTw7FpK28qXNna3F/J515eNDJJZme4E/2pWBt9gEaObZ/jTxX8cPDHxM/bWsfEemjwfJpfiTT9I03Wb3xjoaXGi6dPHa2kN5cW1vNFJIIVW3ZInuY5Lgh3L5kcOPOvGHjT4far4SutN0vwfeWd5GjCx1AXTpIjG3ZGZ181iwa4KzqrvJ5Sr5IZ8vM+74k+K/w08deP9R1K98CrpdjcSXNxaWtgDaW9qht79orVo7eSPzAbqWyfzQyFBDLGAIGhgtdamKc4xhdK1u55+B4fp4SrVxMYVZOopJ3cG1dp6Jbtvo+qd73R9M/H/8Abp+HPir4IW66F4O+Es+oaL4JLeEodW8KaLqU1i58Y3Ft9ieB7IeU66UWu1hHlqu4uY33bm5z/gmV8TPh38M/AP8AxVniHS9It7e9v7nUry5ttEtNQ0eeWO1S1u7S6mvW1e4e1S0eeKLT7EFpruVTKSvHzXb+Ofh/aX8MkXgOb7P9l8mVLjU5p5i7WE0Ttv3rHn7VIkqsIlKpEirtcNI09944+GL3SyWvw+voVWxitzDJrN08bXAiuxJcZ84OMzPZkJuK7IZFwGffU/WpOoqja006/fsaS4eoxwc8FTp1FGUuZv3G+3LrLpq0+7vtoe3/APBTTxh8N/irPpuu+D/GmjeIIPEniDUNXtGtPC1hp01rb3UVos8d/JBdNfB4ZIVdUurCJpGu7lonljRa+mvB/wAUvAGv/BTQ9Sm+KHwf1PxFY6TJaaXc674l0+2aST+xdI0yULFqUltLZWU91p80tzby2Mj3EFxfIqos226/PTUfH3w+FnqDad4JurW9uI3trUzXn2iK2iOjyWm4q5IaY3zJeeYFDIY9innIfcfFbwbc6Ro1s3gPT/tGmJp4lvUUQzXJjtIobxWWMrHIJZYFmR5lkdWnusk+ZH5VRxfLOU3bW3foY4jhp1cLRw0VNKnzWb5L2lutGvK7vrq7bW6X4oa78OdT/a91u81rQfD+i+B9FuSbXSvAzwSWXiNEkDQK89vdXNvaefG6G4kszKluEkSKB5V2t6f8Z/iV8M5/BPw/8FfEKz8L3VxceF9TM+qfDnUrbWF+Gt9ceI9UvrSO0WG7e0u7TyJ4o5rJ5zKtuYzHNHKq+Z4jJ45+FMt7C/8AwrvVoYI57p5Io9cuCZo2eI20ZZ5WI8tFlDMOXMgPGBir4J+I3gvwl4s02+uvBFhqtnFp8kd9aSyTTRz3LXxlSSNZpWERjtVjhXeZ4y255Y7hHeA4qoldXWvr/l/WvU9WpgZVFTk4VE6aVleK1SaTTU3be7vfmfKneKaPcv8AgmB8ZPDfg7V4dJubDTNN8Qtei8u728NpJFq0KXWnXVortdatpiQiwu7EXQMEkk7mTcFEUU4fF/4KWftIfD341fEq+0jwHpVnc6Z4Z1JNN0jWoNIg0yG30yzga0WztPJuZ1ubGaVWvI5WEDKZnHlt5hZfKdJ8e/De20fT7K+8AXeoRWesSag7pqklvc3Fo9lFH9hkm3MXC3MfmCXAIG4okXmyIcDxp4n8N6t4S0TT9D8OyaPeWEs819dy3QuZNQaRYQo3EBlWPyyAvKks8gEZkZASrv2Psrr8f8v69RYfJ4f2q8wcJpu+7jZX3bak5PZJK2idtY6LmKq63/yBbz/r3k/9BNWqq63/AMgS8/695P8A0E1xx3R9VV+B+hj/AA28Ww+Nfh1oup2pXy7u0jLAH/VyKNsif8BcMPfGe9bBlwpFfHn7PP7Q9x8GL6a0uopb7QL5w9xAhHmQPwPNjzxuwACpwGCgZGAR9T+D/id4c8e2sc2la1p91u/5ZGURzp9Y2w4+uMHsSOa7sdgalCbaXu9GfG8H8Y4PNsJCMpqNZJKUW7NtdY9099Ntn57YlZDTkuWJqUQDFKIFrzz7cWN965p1CjaKKBBRRRQAUUUUAFFFFABRRQTgUAFcn8cfGcPgL4T65qEzhZGtntrYZwXnkUogHrgksR/dRvSpvGvxk8MfD6zkl1TWrGOSMZFvFKJrlz2AjUlucYycLnqRXyd8fvj5e/GvXIwsbWWi2JP2S0LZYk8GWQ93PoOFHAydzN6WX4Cdaom1aK3f6HwfGnGWEyvBzpU5qVeSajFO7TenNLslur7vRdWv/9k=", + "id": 1, + "name": "成都实验小学", + "phone": "13919103408", + "province": "四川省", + "status": true + } + ], + "number": 0, + "size": 20, + "totalElements": 1, + "totalPages": 1 + }, + "code": 200, + "desc": "成功", + "success": true +} +``` +# 9. 获取企业列表(管理端其他部分选择企业使用) + +> GET /api/vender/select +> +> 管理端接口 +### 输入: + +``` +keyword=1 // 根据单位名称模糊匹配 +vendors=1,2,3 // 根据ID批量获取单位信息 +``` +### 输出: +``` +{ + "body": [ + { + "account": "xxx", + "category": "小学", + "id": 1, + "name": "成都实验小学" + } + ], + "code": 200, + "desc": "成功", + "success": true +} +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4d41b1b --- /dev/null +++ b/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.7.7 + + com.mathvision.diet + diet + 1.0-SNAPSHOT + pom + + + diet-dao + diet-core + diet-web + + + + 8 + 8 + UTF-8 + + + + + com.alibaba + fastjson + 2.0.32 + + + org.apache.commons + commons-lang3 + 3.12.0 + + + commons-io + commons-io + 2.11.0 + + + org.apache.commons + commons-collections4 + 4.4 + + + org.projectlombok + lombok + 1.18.26 + + + com.google.guava + guava + 31.1-jre + + + ch.qos.logback + logback-classic + + + org.apache.logging.log4j + log4j-to-slf4j + + + org.slf4j + jul-to-slf4j + + + \ No newline at end of file diff --git a/sql/diet.sql b/sql/diet.sql new file mode 100644 index 0000000..311d249 --- /dev/null +++ b/sql/diet.sql @@ -0,0 +1,364 @@ +/* + Navicat Premium Data Transfer + + Source Server : 47.109.27.8 + Source Server Type : MySQL + Source Server Version : 80033 + Source Host : 47.109.27.8:3306 + Source Schema : diet + + Target Server Type : MySQL + Target Server Version : 80033 + File Encoding : 65001 + + Date: 17/09/2023 23:41:50 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for dish +-- ---------------------------- +DROP TABLE IF EXISTS `dish`; +CREATE TABLE `dish` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '菜名', + `vender` bigint NOT NULL COMMENT '单位', + `icon` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT '图片', + `month` json NOT NULL COMMENT '适用月份', + `ingredient` json NOT NULL COMMENT '食材', + `marks` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '标记', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_vender_name`(`vender` ASC, `name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '菜品' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for food_category +-- ---------------------------- +DROP TABLE IF EXISTS `food_category`; +CREATE TABLE `food_category` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `key` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类key', + `name` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类名称', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食物分类' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for food_mark +-- ---------------------------- +DROP TABLE IF EXISTS `food_mark`; +CREATE TABLE `food_mark` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `key` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '标签名称', + `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '标签名称', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食物标签' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for food_nutrient +-- ---------------------------- +DROP TABLE IF EXISTS `food_nutrient`; +CREATE TABLE `food_nutrient` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `key` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类key', + `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类名称', + `measurement` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '计量单位', + `nrv` decimal(8, 2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT 'NRV(营养素参考值)', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_key`(`key` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '营养素' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for ingredient +-- ---------------------------- +DROP TABLE IF EXISTS `ingredient`; +CREATE TABLE `ingredient` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `key` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类key', + `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类名称', + `type` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '分类名称', + `nutrient` json NOT NULL COMMENT '营养素', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_key`(`key` ASC) USING BTREE, + INDEX `idx_type`(`type` ASC) USING BTREE, + INDEX `idx_name`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食材' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for ingredient_mark +-- ---------------------------- +DROP TABLE IF EXISTS `ingredient_mark`; +CREATE TABLE `ingredient_mark` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `ingredient` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '食材', + `vender` bigint UNSIGNED NOT NULL COMMENT '单位', + `mark` enum('常用','忌用') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '常用' COMMENT '标记', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_group_ingredient`(`vender` ASC, `ingredient` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食物标记' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for menu +-- ---------------------------- +DROP TABLE IF EXISTS `menu`; +CREATE TABLE `menu` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `vender` bigint UNSIGNED NOT NULL COMMENT '单位', + `nutrient` bigint UNSIGNED NOT NULL COMMENT '营养标准', + `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '食谱名称', + `day` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '天数', + `meals` json NOT NULL COMMENT '餐次', + `crows` json NOT NULL COMMENT '人群', + `status` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '状态:0-草稿,1-提交审核,2-审核通过,3-审核失败,4-禁用,5-发布', + `approve` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '审批意见', + `start_time` datetime NULL DEFAULT NULL COMMENT '开始时间', + `end_time` datetime NULL DEFAULT NULL COMMENT '结束时间', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食谱' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for menu_approve +-- ---------------------------- +DROP TABLE IF EXISTS `menu_approve`; +CREATE TABLE `menu_approve` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `menu` bigint UNSIGNED NOT NULL COMMENT '食谱', + `approve` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '审批意见', + `pass` bit(1) NOT NULL COMMENT '是否通过,0-不通过,1-通过', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_menu`(`menu` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '审批表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for menu_dish +-- ---------------------------- +DROP TABLE IF EXISTS `menu_dish`; +CREATE TABLE `menu_dish` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `vender` bigint UNSIGNED NOT NULL COMMENT '单位', + `menu` bigint UNSIGNED NOT NULL COMMENT '食谱', + `day` int UNSIGNED NOT NULL DEFAULT 1 COMMENT '天数', + `meal` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '餐次', + `dish` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '菜名', + `ingredient` bigint UNSIGNED NOT NULL COMMENT '食材', + `crow` json NOT NULL COMMENT '人群', + `main` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否主料,0-否,1-是', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_vender`(`vender` ASC) USING BTREE, + INDEX `idx_menu`(`menu` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '食谱内容' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for nutrition +-- ---------------------------- +DROP TABLE IF EXISTS `nutrition`; +CREATE TABLE `nutrition` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `name` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '标准名称', + `vendors` json NOT NULL COMMENT '适用单位', + `food_category_day` json NULL COMMENT '食物种类及数量标准(日)', + `food_category_week` json NULL COMMENT '食物种类及数量标准(周)', + `ingredient` json NULL COMMENT '食材', + `overflow` decimal(5, 2) UNSIGNED NOT NULL DEFAULT 0.00 COMMENT '溢出范围', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_name`(`name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '营养标准' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for role +-- ---------------------------- +DROP TABLE IF EXISTS `role`; +CREATE TABLE `role` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `role_name` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '角色名称', + `role_type` enum('系统','自定义') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '系统' COMMENT '角色类型', + `role_items` json NOT NULL COMMENT '权限项', + `vender` bigint UNSIGNED NOT NULL COMMENT '角色分组', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_vender`(`vender` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 37 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '角色表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for role_item +-- ---------------------------- +DROP TABLE IF EXISTS `role_item`; +CREATE TABLE `role_item` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `item_name` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '权限项名称', + `item_type` enum('管理端','业务端') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '管理端' COMMENT '权限项类型', + `item_value` varchar(255) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '\0' COMMENT '权限项值', + `category` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '权限项分类', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_item_name_type`(`item_type` ASC, `item_name` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 40 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '权限项表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for user +-- ---------------------------- +DROP TABLE IF EXISTS `user`; +CREATE TABLE `user` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '用户名', + `pwd` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '密码', + `status` bit(1) NOT NULL DEFAULT b'1' COMMENT '状态', + `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '名字', + `phone` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '电话', + `gender` enum('男','女') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL, + `email` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '邮箱', + `address` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '地址', + `flag` bit(32) NULL DEFAULT NULL COMMENT '用户打标', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_uid`(`uid` ASC) USING BTREE, + UNIQUE INDEX `udx_phone`(`phone` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for user_log +-- ---------------------------- +DROP TABLE IF EXISTS `user_log`; +CREATE TABLE `user_log` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '用户', + `client_type` enum('web','android','ios') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT 'web' COMMENT '客户端类型', + `client_version` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL DEFAULT '1.0' COMMENT '客户端版本', + `login` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '登录时间', + `logout` datetime NULL DEFAULT NULL COMMENT '登出时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_uid_client_type`(`uid` ASC, `client_type` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 101 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '登录日志表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for user_message +-- ---------------------------- +DROP TABLE IF EXISTS `user_message`; +CREATE TABLE `user_message` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `type` enum('code','notify','message') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `content` varchar(128) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL, + `status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '状态,0-未读,1-已读', + `operate` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作人', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + INDEX `idx_uid`(`uid` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '用户消息表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for user_role +-- ---------------------------- +DROP TABLE IF EXISTS `user_role`; +CREATE TABLE `user_role` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '用户名', + `role_id` int UNSIGNED NOT NULL COMMENT '角色ID', + `vender` bigint UNSIGNED NOT NULL COMMENT '单位', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作人', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_uid`(`uid` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '用户商家权限分配表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for user_session +-- ---------------------------- +DROP TABLE IF EXISTS `user_session`; +CREATE TABLE `user_session` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `uid` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '用户', + `vender` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '鉴权', + `client_type` enum('web','android','ios') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '客户端类型', + `client_version` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '客户端版本', + `expired_time` bigint NOT NULL COMMENT '过期时间', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_uid_client_type`(`uid` ASC, `client_type` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '用户会话表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for vender +-- ---------------------------- +DROP TABLE IF EXISTS `vender`; +CREATE TABLE `vender` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `name` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '单位名称', + `account` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '初始账号', + `category` enum('学校','医院','事业单位','其他') CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '单位类别', + `status` bit(1) NOT NULL DEFAULT b'1' COMMENT '状态,1-正常,0-停用', + `icon` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL COMMENT '图标', + `url` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '单位主页', + `province` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '省', + `city` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '市', + `area` varchar(20) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '区', + `address` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '地址', + `phone` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '电话', + `email` varchar(32) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '邮箱', + `contacts` varchar(16) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '联络人', + `operate` varchar(18) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '操作人', + `expire` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '过期时间', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_name`(`name` ASC) USING BTREE, + UNIQUE INDEX `udx_account`(`account` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '商家表' ROW_FORMAT = Dynamic; + +-- ---------------------------- +-- Table structure for vender_config +-- ---------------------------- +DROP TABLE IF EXISTS `vender_config`; +CREATE TABLE `vender_config` ( + `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT, + `vender` bigint UNSIGNED NOT NULL COMMENT '单位', + `breakfast` decimal(5, 2) UNSIGNED NULL DEFAULT 0.00 COMMENT '早餐能量、营养摄入比例', + `lunch` decimal(5, 2) NULL DEFAULT NULL COMMENT '午餐能量、营养摄入比例', + `dinner` decimal(5, 2) NULL DEFAULT NULL COMMENT '晚餐能量、营养摄入比例', + `operate` varchar(45) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT 'system' COMMENT '操作员', + `created` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modify` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `udx_vender`(`vender` ASC) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 21 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '人群配置表' ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/sql/init.sql b/sql/init.sql new file mode 100644 index 0000000..9fcf81b --- /dev/null +++ b/sql/init.sql @@ -0,0 +1,133 @@ +/* + Navicat Premium Data Transfer + + Source Server : 47.109.27.8 + Source Server Type : MySQL + Source Server Version : 80033 + Source Host : 47.109.27.8:3306 + Source Schema : diet + + Target Server Type : MySQL + Target Server Version : 80033 + File Encoding : 65001 + + Date: 10/09/2023 23:53:18 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Records of food_category +-- ---------------------------- +BEGIN; +INSERT INTO `food_category` VALUES (1, '谷类', '谷类', 'system', '2023-09-04 00:27:22', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (2, '薯类', '薯类', 'system', '2023-09-04 00:27:51', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (3, '蔬菜类', '蔬菜类', 'system', '2023-09-04 00:27:59', '2023-09-04 00:27:59'); +INSERT INTO `food_category` VALUES (4, '菌藻类', '菌藻类', 'system', '2023-09-04 00:28:09', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (5, '水果类', '水果类', 'system', '2023-09-04 00:28:18', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (6, '畜肉类', '畜肉类', 'system', '2023-09-04 00:31:58', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (7, '禽肉类', '禽肉类', 'system', '2023-09-04 00:32:09', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (8, '鱼虾类', '鱼虾类', 'system', '2023-09-04 00:32:28', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (9, '蛋类', '蛋类', 'system', '2023-09-04 00:32:34', '2023-09-10 22:56:23'); +INSERT INTO `food_category` VALUES (10, '奶及奶制品', '奶及奶制品', 'system', '2023-09-04 00:32:46', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (11, '大豆类及其制品', '大豆类及其制品', 'system', '2023-09-10 22:54:49', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (12, '坚果', '坚果', 'system', '2023-09-10 22:54:56', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (13, '烹调油', '烹调油', 'system', '2023-09-10 22:55:01', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (14, '调味品', '调味品', 'system', '2023-09-10 22:55:05', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (15, '婴幼儿食品', '婴幼儿食品', 'system', '2023-09-10 22:55:13', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (16, '小吃、甜饼', '小吃、甜饼', 'system', '2023-09-10 22:55:19', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (17, '速食食品', '速食食品', 'system', '2023-09-10 22:55:25', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (18, '饮料类', '饮料类', 'system', '2023-09-10 22:55:29', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (19, '含酒精饮料', '含酒精饮料', 'system', '2023-09-10 22:55:51', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (20, '糖类', '糖类', 'system', '2023-09-10 22:55:54', '2023-09-10 22:56:24'); +INSERT INTO `food_category` VALUES (21, '其他', '其他', 'system', '2023-09-10 22:55:58', '2023-09-10 22:56:24'); +COMMIT; + +-- ---------------------------- +-- Records of food_mark +-- ---------------------------- +BEGIN; +INSERT INTO `food_mark` VALUES (1, '主荤', '主荤', 'system', '2017-08-19 02:49:17', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (2, '次荤', '次荤', 'system', '2020-06-20 17:51:33', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (3, '素菜', '素菜', 'system', '2009-10-08 15:08:52', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (4, '主食', '主食', 'system', '2010-09-14 14:04:33', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (5, '辅食', '辅食', 'system', '2014-04-30 20:52:34', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (6, '面食', '面食', 'system', '2000-04-04 16:27:00', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (7, '奶', '奶', 'system', '2016-05-22 20:43:52', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (8, '水果', '水果', 'system', '2012-01-17 05:35:52', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (9, '汤类', '汤类', 'system', '2009-10-09 03:38:01', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (10, '粥类', '粥类', 'system', '2011-01-09 15:58:14', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (11, '糕点', '糕点', 'system', '2008-06-25 17:18:10', '2023-09-10 23:00:00'); +INSERT INTO `food_mark` VALUES (12, '饮料类', '饮料类', 'system', '2012-12-09 19:19:39', '2023-09-10 23:00:00'); +COMMIT; + +-- ---------------------------- +-- Records of food_nutrient +-- ---------------------------- +BEGIN; +INSERT INTO `food_nutrient` VALUES (1, 'energy', '能量kcal', 'kcal', 2000.00, 'system', '2012-07-08 19:18:17', '2023-09-10 23:19:01'); +INSERT INTO `food_nutrient` VALUES (2, 'protein', '蛋白质', 'g', 60.00, 'system', '2017-12-21 15:42:51', '2023-09-10 23:31:35'); +INSERT INTO `food_nutrient` VALUES (3, 'fat', '脂肪', 'g', 60.00, 'system', '2018-12-13 07:28:46', '2023-09-10 23:33:17'); +INSERT INTO `food_nutrient` VALUES (4, 'carbs', '碳水化合物', 'g', 300.00, 'system', '2022-12-17 09:39:38', '2023-09-10 23:33:30'); +INSERT INTO `food_nutrient` VALUES (5, 'calcium', '钙', 'mg', 800.00, 'system', '2017-01-06 11:53:56', '2023-09-10 23:33:46'); +INSERT INTO `food_nutrient` VALUES (6, 'iron', '铁', 'mg', 15.00, 'system', '2022-03-05 18:53:22', '2023-09-10 23:34:10'); +INSERT INTO `food_nutrient` VALUES (7, 'zinc', '锌', 'mg', 15.00, 'system', '2011-04-17 21:15:04', '2023-09-10 23:34:21'); +INSERT INTO `food_nutrient` VALUES (8, 'va', '维生素A', 'μgRAE', 800.00, 'system', '2015-08-06 20:42:28', '2023-09-10 23:34:31'); +INSERT INTO `food_nutrient` VALUES (9, 'vb1', '维生素B1', 'mg', 1.40, 'system', '2022-11-30 07:59:13', '2023-09-10 23:34:48'); +INSERT INTO `food_nutrient` VALUES (10, 'vb2', '维生素B2', 'mg', 1.40, 'system', '2020-10-08 18:13:27', '2023-09-10 23:34:56'); +INSERT INTO `food_nutrient` VALUES (11, 'vc', '维生素C', 'mg', 100.00, 'system', '2020-07-01 21:50:49', '2023-09-10 23:35:08'); +INSERT INTO `food_nutrient` VALUES (12, 'fiber', '膳食纤维', 'g', 25.00, 'system', '2003-01-29 16:56:24', '2023-09-10 23:35:12'); +COMMIT; + +-- ---------------------------- +-- Records of role +-- ---------------------------- +BEGIN; +INSERT INTO `role` VALUES (1, '超级管理员', '系统', '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]', 0, 'system', '2023-09-02 16:21:02', '2023-09-06 22:29:20'); +COMMIT; + +-- ---------------------------- +-- Records of role_item +-- ---------------------------- +BEGIN; +INSERT INTO `role_item` VALUES (1, '食材列表-查看', '管理端', '[get]|:ingredient', '食材管理', 'system', '2023-09-02 16:03:16', '2023-09-03 01:46:10'); +INSERT INTO `role_item` VALUES (2, '食材-新增/编辑/删除', '管理端', '[post,put,delete]:ingredient', '食材管理', 'system', '2023-09-03 00:32:49', '2023-09-03 01:46:29'); +INSERT INTO `role_item` VALUES (3, '菜品列表-查看', '管理端', '[get]:dish', '菜品管理', 'system', '2023-09-03 00:43:38', '2023-09-03 01:46:43'); +INSERT INTO `role_item` VALUES (4, '菜品-新增/编辑/删除', '管理端', '[post,put,delete]:dish', '菜品管理', 'system', '2023-09-03 00:44:17', '2023-09-08 03:26:52'); +INSERT INTO `role_item` VALUES (5, '食谱列表-查看', '管理端', '[get]:menu', '食谱管理', 'system', '2023-09-03 00:47:19', '2023-09-03 01:47:15'); +INSERT INTO `role_item` VALUES (6, '食谱-新增/编辑/删除', '管理端', '[post,put,delete]:menu', '食谱管理', 'system', '2023-09-03 00:48:15', '2023-09-03 01:49:30'); +INSERT INTO `role_item` VALUES (7, '食谱审核列表-查看', '管理端', '[get]:menu/review', '食谱管理', 'system', '2023-09-03 00:50:30', '2023-09-12 01:24:38'); +INSERT INTO `role_item` VALUES (8, '食谱审核列表-通过/驳回', '管理端', '[put]:menu/review', '食谱管理', 'system', '2023-09-03 00:51:10', '2023-09-12 01:24:41'); +INSERT INTO `role_item` VALUES (9, '食谱发布计划-查看', '管理端', '[get]:menu/release', '食谱管理', 'system', '2023-09-03 00:52:11', '2023-09-12 01:24:45'); +INSERT INTO `role_item` VALUES (10, '食谱发布计划-发布/取消发布', '管理端', '[put]:menu/release', '食谱管理', 'system', '2023-09-03 00:52:44', '2023-09-12 01:24:54'); +INSERT INTO `role_item` VALUES (11, '营养标准模型-查看', '管理端', '[get]:nutrition', '人群营养标准管理管理', 'system', '2023-09-03 00:55:17', '2023-09-04 01:58:00'); +INSERT INTO `role_item` VALUES (12, '营养标准模型-新增/编辑/删除', '管理端', '[post,put,delete]:nutrition', '人群营养标准管理管理', 'system', '2023-09-03 00:55:17', '2023-09-04 01:58:02'); +INSERT INTO `role_item` VALUES (13, '单位列表-查看', '管理端', '[get]:vender', '单位管理', 'system', '2023-09-03 00:58:43', '2023-09-03 01:54:37'); +INSERT INTO `role_item` VALUES (14, '单位-新增/编辑/启用/禁用/删除', '管理端', '[post,put,delete]:vender', '单位管理', 'system', '2023-09-03 00:58:43', '2023-09-03 01:54:45'); +INSERT INTO `role_item` VALUES (15, '用户列表-查看', '管理端', '[get]:user', '系统设置', 'system', '2023-09-03 01:05:02', '2023-09-08 03:26:30'); +INSERT INTO `role_item` VALUES (16, '用户-新增/编辑/删除', '管理端', '[post,put,delete]:user', '系统设置', 'system', '2023-09-03 01:06:28', '2023-09-03 01:55:55'); +INSERT INTO `role_item` VALUES (17, '角色权限-查看/新增/编辑/删除', '管理端', '[get,post,put,delete]:role', '系统设置', 'system', '2023-09-03 01:05:02', '2023-09-08 03:25:32'); +INSERT INTO `role_item` VALUES (18, '使用流程', '业务端', '[get]:word', '基础权限', 'system', '2023-09-03 01:09:06', '2023-09-03 01:56:45'); +INSERT INTO `role_item` VALUES (19, '数据大屏-大屏显示', '业务端', '[get]:dashboard', '基础权限', 'system', '2023-09-03 01:24:37', '2023-09-03 01:56:56'); +INSERT INTO `role_item` VALUES (20, '数据大屏-大屏显示(LED)', '业务端', '[get]:dashboard/led', '基础权限', 'system', '2023-09-03 01:24:37', '2023-09-12 01:25:01'); +INSERT INTO `role_item` VALUES (21, '配餐设置-查看', '业务端', '[get]:vender/config', '配餐设置', 'system', '2023-09-03 01:26:24', '2023-09-12 20:36:41'); +INSERT INTO `role_item` VALUES (22, '配餐设置-编辑', '业务端', '[post]:vender/config', '配餐设置', 'system', '2023-09-03 01:29:20', '2023-09-17 19:45:01'); +INSERT INTO `role_item` VALUES (23, '食材列表-查看', '业务端', '[get]:ingredient', '食材管理', 'system', '2023-09-02 16:03:16', '2023-09-03 02:00:23'); +INSERT INTO `role_item` VALUES (24, '食材-常用/忌用', '业务端', '[put,delete]:ingredient/mark', '食材管理', 'system', '2023-09-02 16:03:16', '2023-09-15 02:21:31'); +INSERT INTO `role_item` VALUES (25, '菜品列表-查看', '业务端', '[get]:dish', '菜品管理', 'system', '2023-09-03 00:43:38', '2023-09-03 02:00:56'); +INSERT INTO `role_item` VALUES (26, '菜品-新增/编辑/删除', '业务端', '[post,put,delete]:dish', '菜品管理', 'system', '2023-09-03 00:44:17', '2023-09-08 03:25:51'); +INSERT INTO `role_item` VALUES (27, '食谱列表-查看', '业务端', '[get]:menu', '食谱管理', 'system', '2023-09-03 02:02:07', '2023-09-03 02:02:07'); +INSERT INTO `role_item` VALUES (28, '食谱-新增/编辑/删除', '业务端', '[post,put,delete]:menu', '食谱管理', 'system', '2023-09-03 02:02:50', '2023-09-10 12:26:37'); +INSERT INTO `role_item` VALUES (29, '食谱审核记录-查看', '业务端', '[get]:menu/review', '食谱管理', 'system', '2023-09-03 02:03:31', '2023-09-12 02:53:53'); +INSERT INTO `role_item` VALUES (30, '食谱发布计划-查看', '业务端', '[get]:menu/release', '食谱管理', 'system', '2023-09-14 23:37:14', '2023-09-14 23:52:17'); +INSERT INTO `role_item` VALUES (31, '食谱发布计划-发布/取消发布', '业务端', '[put]:menu/release', '食谱管理', 'system', '2023-09-14 23:37:54', '2023-09-14 23:52:39'); +INSERT INTO `role_item` VALUES (32, '营养标准模型-查看', '业务端', '[get]:nutrition', '人群营养标准管理管理', 'system', '2023-09-03 00:55:17', '2023-09-14 23:55:36'); +INSERT INTO `role_item` VALUES (33, '单位基础信息-查看', '业务端', '[get]:vender', '基础信息管理', 'system', '2023-09-03 02:04:29', '2023-09-14 23:55:45'); +INSERT INTO `role_item` VALUES (34, '单位基础信息-修改', '业务端', '[post]:vender', '基础信息管理', 'system', '2023-09-03 02:05:35', '2023-09-14 23:55:47'); +INSERT INTO `role_item` VALUES (35, '用户列表-查看', '业务端', '[get]:user', '系统设置', 'system', '2023-09-03 01:05:02', '2023-09-14 23:55:51'); +INSERT INTO `role_item` VALUES (36, '角色权限-查看/新增/编辑/删除', '业务端', '[get,post,put,delete]:role', '系统设置', 'system', '2023-09-03 01:05:02', '2023-09-14 23:55:53'); +INSERT INTO `role_item` VALUES (37, '用户-新增/编辑/删除', '业务端', '[post,put,delete]:user', '系统设置', 'system', '2023-09-03 01:06:28', '2023-09-14 23:55:57'); +COMMIT; + +SET FOREIGN_KEY_CHECKS = 1;