This commit is contained in:
dzc committed 2026-07-22 18:56:09 +08:00
commit e2d28b5d53
13 files changed
+450 -64

No files matched your search

@@ -0,0 +1,48 @@
package com.bsd.pjcloud.canteen.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
@Data
@TableName("canteen_online_combo_item")
@EqualsAndHashCode(callSuper = true)
@Schema(description = "线上点餐套餐组成明细")
public class CanteenOnlineComboItemEntity extends Model<CanteenOnlineComboItemEntity> {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "套餐 SPU id")
private Long comboSpuId;
@Schema(description = "组成菜品 SPU id")
private Long childSpuId;
@Schema(description = "份数")
private Integer quantity;
@Schema(description = "排序")
private Integer sortOrder;
private LocalDateTime createTime;
@TableField(exist = false)
@Schema(description = "组成菜品名称")
private String childSpuName;
@TableField(exist = false)
@Schema(description = "组成菜品封面图")
private String childSpuPicUrl;
@TableField(exist = false)
@Schema(description = "组成菜品价格(分)")
private Integer childSpuPrice;
}
@@ -10,7 +10,9 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.bsd.pjcloud.common.core.util.TenantTable;
import java.time.LocalDateTime;
import java.util.List;
/**
* 在线点餐spu
@@ -80,9 +82,15 @@ public class CanteenOnlineSpuEntity extends Model<CanteenOnlineSpuEntity> {
/**
* 商品状态: 0 上架(默认/开启) 1 下架(禁用) -1 回收
*/
@Schema(description="商品状态: 0 上架(默认/开启) 1 下架(禁用) -1 回收")
@Schema(description="商品状态: 0 上架(默认/开启) 1 下架(禁用) -1 回收")
private Integer status;
/**
* 商品类型:1单品 2套餐
*/
@Schema(description = "商品类型:1单品 2套餐")
private Integer spuType;
/**
* 是否必选:0 否 1 是(前端下单需包含该品,如米饭)
*/
@@ -212,4 +220,11 @@ public class CanteenOnlineSpuEntity extends Model<CanteenOnlineSpuEntity> {
@Schema(description="菜品类型名称")
@TableField(exist = false)
private String dishTypeName;
/**
* 套餐组成(仅 spuType=2 时使用)
*/
@Schema(description = "套餐组成明细")
@TableField(exist = false)
private List<CanteenOnlineComboItemEntity> comboItems;
}
@@ -1,16 +1,12 @@
package com.bsd.pjcloud.canteen.vo;
import com.bsd.pjcloud.canteen.vo.app.OnlineSpuChoiceGroupRuleVO;
import com.baomidou.mybatisplus.annotation.*;
import com.bsd.pjcloud.common.core.util.MoneyDeserializer;
import com.bsd.pjcloud.common.core.util.MoneySerializer;
import com.bsd.pjcloud.common.core.util.TenantTable;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.bsd.pjcloud.canteen.vo.app.OnlineSpuComboItemVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* 在线点餐spu
@@ -19,7 +15,6 @@ import java.time.LocalDateTime;
* @date 2024-12-16 23:42:19
*/
@Data
@TenantTable
@Schema(description = "在线点餐vo")
public class APPOnlineSpuVO {
@@ -109,6 +104,12 @@ public class APPOnlineSpuVO {
@Schema(description = "商品状态: 0 上架 1 下架")
private Integer status;
@Schema(description = "商品类型:1单品 2套餐")
private Integer spuType;
@Schema(description = "套餐组成(spuType=2 时返回)")
private List<OnlineSpuComboItemVO> comboItems;
/**
* 是否必选:0 否 1 是
*/
@@ -150,7 +151,6 @@ public class APPOnlineSpuVO {
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description="创建时间")
private LocalDateTime createTime;
@@ -0,0 +1,27 @@
package com.bsd.pjcloud.canteen.vo.app;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "套餐组成项(App)")
public class OnlineSpuComboItemVO {
@Schema(description = "组成菜品 SPU id")
private Long childSpuId;
@Schema(description = "组成菜品名称")
private String name;
@Schema(description = "组成菜品封面")
private String picUrl;
@Schema(description = "份数")
private Integer quantity;
@Schema(description = "排序")
private Integer sortOrder;
@Schema(description = "组成菜品单价(分,仅展示)")
private Integer price;
}
@@ -10,12 +10,15 @@ public class CanteenOnlineSpuSimpleVO {
@Schema(description = "id")
private Long id;
@Schema(description = "菜品名称")
@Schema(description = "商品名称")
private String name;
@Schema(description = "菜品封面图")
@Schema(description = "商品封面图")
private String picUrl;
@Schema(description = "商品价格,单位:分")
private Integer price;
@Schema(description = "关联线下菜品id")
private Long linkSpuId;
@@ -95,6 +95,7 @@ public class CanteenOnlineSpuController {
LambdaQueryWrapper<CanteenOnlineSpuEntity> wrapper = Wrappers.lambdaQuery();
wrapper.like(StrUtil.isNotBlank(canteenOnlineSpu.getName()), CanteenOnlineSpuEntity::getName, canteenOnlineSpu.getName());
wrapper.eq(Objects.nonNull(canteenOnlineSpu.getCategoryId()), CanteenOnlineSpuEntity::getCategoryId, canteenOnlineSpu.getCategoryId());
wrapper.eq(Objects.nonNull(canteenOnlineSpu.getSpuType()), CanteenOnlineSpuEntity::getSpuType, canteenOnlineSpu.getSpuType());
// 根据菜品类型筛选
if (Objects.nonNull(dishType)) {
@@ -106,7 +107,11 @@ public class CanteenOnlineSpuController {
wrapper.isNotNull(CanteenOnlineSpuEntity::getLinkSpuId);
}
}
page.orders().clear();
wrapper.orderByDesc(CanteenOnlineSpuEntity::getCreateTime)
.orderByDesc(CanteenOnlineSpuEntity::getId);
return R.ok(canteenOnlineSpuService.pageScope(page, wrapper));
}
@@ -117,11 +122,11 @@ public class CanteenOnlineSpuController {
* @param id id
* @return R
*/
@Operation(summary = "通过id查询", description = "通过id查询")
@Operation(summary = "通过id查询", description = "通过id查询(含套餐组成)")
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('canteen_canteenOnlineSpu_view')")
public R getById(@PathVariable("id") Long id) {
return R.ok(canteenOnlineSpuService.getById(id));
return R.ok(canteenOnlineSpuService.getDetail(id));
}
/**
@@ -130,20 +135,26 @@ public class CanteenOnlineSpuController {
* @param canteenOnlineSpu 在线点餐spu
* @return R
*/
@Operation(summary = "新增在线点餐spu", description = "新增在线点餐spu")
@Operation(summary = "新增在线点餐spu", description = "新增在线点餐spu;套餐时传 spuType=2 与 comboItems")
@SysLog("新增在线点餐spu")
@ApiCryptoAes
@PostMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenOnlineSpu_add')")
public R save(@RequestBody CanteenOnlineSpuEntity canteenOnlineSpu) {
if (canteenOnlineSpu.getChoiceGroupId() != null) {
if (canteenOnlineSpu.getChoiceGroupId() != null
&& !Objects.equals(canteenOnlineSpu.getSpuType(), 2)) {
CanteenOnlineSpuChoiceGroupEntity g = canteenOnlineSpuChoiceGroupService.getById(canteenOnlineSpu.getChoiceGroupId());
if (g == null) {
return R.failed("择一分组不存在(可能是前端 long 精度丢失导致 id 被圆整),请重新选择分组");
}
}
canteenOnlineSpu.setDeptId(SecurityUtils.getUser().getDeptId());
return R.ok(canteenOnlineSpuService.save(canteenOnlineSpu));
try {
return R.ok(canteenOnlineSpuService.saveDeep(canteenOnlineSpu));
}
catch (IllegalArgumentException ex) {
return R.failed(ex.getMessage());
}
}
@Operation(summary = "批量新增关联每日菜谱", description = "批量新增关联每日菜谱")
@@ -161,19 +172,25 @@ public class CanteenOnlineSpuController {
* @param canteenOnlineSpu 在线点餐spu
* @return R
*/
@Operation(summary = "修改在线点餐spu", description = "修改在线点餐spu")
@Operation(summary = "修改在线点餐spu", description = "修改在线点餐spu;套餐时传 spuType=2 与 comboItems")
@SysLog("修改在线点餐spu")
@ApiCryptoAes
@PutMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenOnlineSpu_edit')")
public R updateById(@RequestBody CanteenOnlineSpuEntity canteenOnlineSpu) {
if (canteenOnlineSpu.getChoiceGroupId() != null) {
if (canteenOnlineSpu.getChoiceGroupId() != null
&& !Objects.equals(canteenOnlineSpu.getSpuType(), 2)) {
CanteenOnlineSpuChoiceGroupEntity g = canteenOnlineSpuChoiceGroupService.getById(canteenOnlineSpu.getChoiceGroupId());
if (g == null) {
return R.failed("择一分组不存在(可能是前端 long 精度丢失导致 id 被圆整),请重新选择分组");
}
}
return R.ok(canteenOnlineSpuService.updateById(canteenOnlineSpu));
try {
return R.ok(canteenOnlineSpuService.updateDeep(canteenOnlineSpu));
}
catch (IllegalArgumentException ex) {
return R.failed(ex.getMessage());
}
}
/**
@@ -186,8 +203,14 @@ public class CanteenOnlineSpuController {
@SysLog("通过id删除在线点餐spu")
@DeleteMapping
@PreAuthorize("@pms.hasPermission('canteen_canteenOnlineSpu_del')")
@Transactional(rollbackFor = Exception.class)
public R removeById(@RequestBody Long[] ids) {
return R.ok(canteenOnlineSpuService.removeBatchByIds(CollUtil.toList(ids)));
List<Long> idList = CollUtil.toList(ids);
boolean ok = canteenOnlineSpuService.removeBatchByIds(idList);
if (ok && CollUtil.isNotEmpty(idList)) {
canteenOnlineSpuService.removeComboItemsByComboIds(idList);
}
return R.ok(ok);
}
@@ -244,7 +267,7 @@ public class CanteenOnlineSpuController {
*/
@ApiCryptoAes
@GetMapping("/app/getCanteenSpuList")
@Operation(summary = "获取在线点餐的菜品列表", description = "商户 id 必传;返回含 status、requiredChoice、choiceGroupId+choiceGroup(择一/组合规则)、soldOut")
@Operation(summary = "获取在线点餐的菜品列表", description = "商户 id 必传;返回含 status、spuType、comboItems(套餐组成)、requiredChoice、choiceGroup、soldOut")
public R<List<APPOnlineSpuVO>> getCanteenSpuList(
@RequestParam(value = "categoryId", required = false) @Parameter(description = "分类 id,可选") String categoryId,
@RequestParam(value = "merchantId") @Parameter(description = "商户 id,必填") String merchantId,
@@ -262,16 +285,21 @@ public class CanteenOnlineSpuController {
@ApiCryptoAes
@GetMapping("/app/getCanteenSpuDetails")
@Operation(summary = "获取详情接口", description = "通过spu的id")
@Operation(summary = "获取详情接口", description = "通过spu的id;套餐含 comboItems 组成")
public R<APPOnlineSpuVO> getCanteenSpuDetails(@RequestParam(value = "id") Long id) {
CanteenOnlineSpuEntity canteenOnlineSpuEntity = canteenOnlineSpuService.getById(id);
// 走 getDetail,保证套餐组成从明细表装载
CanteenOnlineSpuEntity canteenOnlineSpuEntity = canteenOnlineSpuService.getDetail(id);
if (canteenOnlineSpuEntity == null) {
return R.ok(null);
}
APPOnlineSpuVO bean = BeanUtils.toBean(canteenOnlineSpuEntity, APPOnlineSpuVO.class);
if (bean != null && canteenOnlineSpuEntity != null) {
Integer st = canteenOnlineSpuEntity.getStock();
bean.setSoldOut(st == null || st <= 0);
canteenOnlineSpuService.fillAppOnlineSpuChoiceGroups(Collections.singletonList(bean),
Collections.singletonList(canteenOnlineSpuEntity));
}
Integer st = canteenOnlineSpuEntity.getStock();
bean.setSoldOut(st == null || st <= 0);
canteenOnlineSpuService.fillAppOnlineSpuChoiceGroups(Collections.singletonList(bean),
Collections.singletonList(canteenOnlineSpuEntity));
// BeanUtils 无法正确把 Entity 明细转到 App VO,统一用 fill 覆盖
canteenOnlineSpuService.fillAppComboItems(Collections.singletonList(bean),
Collections.singletonList(canteenOnlineSpuEntity));
return R.ok(bean);
}
@@ -0,0 +1,9 @@
package com.bsd.pjcloud.canteen.mapper;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineComboItemEntity;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CanteenOnlineComboItemMapper extends PjcloudBaseMapper<CanteenOnlineComboItemEntity> {
}
@@ -17,6 +17,29 @@ import java.util.List;
public interface CanteenOnlineSpuService extends IService<CanteenOnlineSpuEntity> {
/**
* 详情(含套餐组成)
*/
CanteenOnlineSpuEntity getDetail(Long id);
/**
* 新增(套餐时同步组成)
*/
Boolean saveDeep(CanteenOnlineSpuEntity entity);
/**
* 修改(套餐时同步组成)
*/
Boolean updateDeep(CanteenOnlineSpuEntity entity);
/**
* 为 App VO 批量填充套餐组成
*/
void fillAppComboItems(List<APPOnlineSpuVO> vos, List<CanteenOnlineSpuEntity> entities);
/** 删除套餐组成明细 */
void removeComboItemsByComboIds(List<Long> comboIds);
/**
* 模板选择线上菜品分页(简化数据,用于选择弹窗)
*/
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.canteen.service.impl;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.canteen.dto.CanteenOnlineSpuShelfReqDTO;
@@ -13,17 +14,20 @@ import com.bsd.pjcloud.canteen.dto.canteenonline.CanteenOnlineSpuSaveItemDto;
import com.bsd.pjcloud.canteen.entity.CanteenCategoryEntity;
import com.bsd.pjcloud.canteen.entity.CanteenDailyMenuEntity;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineCategoryEntity;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineComboItemEntity;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuChoiceGroupEntity;
import com.bsd.pjcloud.canteen.entity.CanteenOnlineSpuEntity;
import com.bsd.pjcloud.canteen.mapper.CanteenCategoryMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenDailyMenuMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenOnlineCategoryMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenOnlineComboItemMapper;
import com.bsd.pjcloud.canteen.mapper.CanteenOnlineSpuMapper;
import com.bsd.pjcloud.canteen.service.CanteenOnlineSpuChoiceGroupService;
import com.bsd.pjcloud.canteen.service.CanteenOnlineSpuService;
import com.bsd.pjcloud.canteen.util.MealSegmentCategoryUtils;
import com.bsd.pjcloud.canteen.vo.app.CanteenOnlineSpuAppVO;
import com.bsd.pjcloud.canteen.vo.app.OnlineSpuChoiceGroupRuleVO;
import com.bsd.pjcloud.canteen.vo.app.OnlineSpuComboItemVO;
import com.bsd.pjcloud.canteen.vo.APPOnlineSpuVO;
import com.bsd.pjcloud.canteen.vo.erp.CanteenOnlineSpuSimpleVO;
import com.bsd.pjcloud.common.core.util.BeanUtils;
@@ -37,8 +41,12 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -70,6 +78,214 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
private final CanteenOnlineSpuMapper baseMapper;
private final CanteenOnlineSpuChoiceGroupService canteenOnlineSpuChoiceGroupService;
private final CanteenOnlineComboItemMapper comboItemMapper;
@Override
public CanteenOnlineSpuEntity getDetail(Long id) {
CanteenOnlineSpuEntity entity = baseMapper.selectById(id);
if (entity == null) {
return null;
}
if (entity.getSpuType() == null) {
entity.setSpuType(1);
}
if (Objects.equals(entity.getSpuType(), 2)) {
entity.setComboItems(loadComboItems(Collections.singletonList(id)).getOrDefault(id, Collections.emptyList()));
}
else {
entity.setComboItems(Collections.emptyList());
}
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean saveDeep(CanteenOnlineSpuEntity entity) {
normalizeSpuType(entity);
validateCombo(entity, null);
baseMapper.insert(entity);
saveComboItems(entity.getId(), entity.getSpuType(), entity.getComboItems());
return Boolean.TRUE;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateDeep(CanteenOnlineSpuEntity entity) {
normalizeSpuType(entity);
validateCombo(entity, entity.getId());
baseMapper.updateById(entity);
comboItemMapper.delete(Wrappers.<CanteenOnlineComboItemEntity>lambdaQuery()
.eq(CanteenOnlineComboItemEntity::getComboSpuId, entity.getId()));
saveComboItems(entity.getId(), entity.getSpuType(), entity.getComboItems());
return Boolean.TRUE;
}
@Override
public void removeComboItemsByComboIds(List<Long> comboIds) {
if (CollUtil.isEmpty(comboIds)) {
return;
}
comboItemMapper.delete(Wrappers.<CanteenOnlineComboItemEntity>lambdaQuery()
.in(CanteenOnlineComboItemEntity::getComboSpuId, comboIds));
}
@Override
public void fillAppComboItems(List<APPOnlineSpuVO> vos, List<CanteenOnlineSpuEntity> entities) {
if (CollUtil.isEmpty(vos) || CollUtil.isEmpty(entities)) {
return;
}
List<Long> comboIds = new ArrayList<>();
for (CanteenOnlineSpuEntity e : entities) {
if (resolveSpuType(e) == 2 && e.getId() != null) {
comboIds.add(e.getId());
}
}
Map<Long, List<CanteenOnlineComboItemEntity>> comboMap = loadComboItems(comboIds);
for (int i = 0; i < vos.size() && i < entities.size(); i++) {
CanteenOnlineSpuEntity entity = entities.get(i);
int type = resolveSpuType(entity);
vos.get(i).setSpuType(type);
if (type == 2) {
List<CanteenOnlineComboItemEntity> lines = comboMap.getOrDefault(entity.getId(), Collections.emptyList());
// getDetail 已装载时直接用,避免 map 再查空
if (CollUtil.isEmpty(lines) && CollUtil.isNotEmpty(entity.getComboItems())) {
lines = entity.getComboItems();
}
vos.get(i).setComboItems(toAppComboVos(lines));
}
else {
vos.get(i).setComboItems(Collections.emptyList());
}
}
}
private int resolveSpuType(CanteenOnlineSpuEntity entity) {
Integer type = entity.getSpuType();
if (type == null) {
return 1;
}
int v = type.intValue();
return (v == 2) ? 2 : 1;
}
private void normalizeSpuType(CanteenOnlineSpuEntity entity) {
if (entity.getSpuType() == null || (entity.getSpuType() != 1 && entity.getSpuType() != 2)) {
entity.setSpuType(1);
}
if (Objects.equals(entity.getSpuType(), 2)) {
// 套餐不参与择一/单品必选
entity.setRequiredChoice(0);
entity.setChoiceGroupId(null);
entity.setLinkSpuId(null);
}
else if (entity.getRequiredChoice() == null) {
entity.setRequiredChoice(0);
}
}
private void validateCombo(CanteenOnlineSpuEntity entity, Long selfId) {
if (!Objects.equals(entity.getSpuType(), 2)) {
return;
}
List<CanteenOnlineComboItemEntity> items = entity.getComboItems();
if (CollUtil.isEmpty(items) || items.stream().filter(i -> i.getChildSpuId() != null).count() < 2) {
throw new IllegalArgumentException("套餐至少选择 2 个组成菜品");
}
Set<Long> childIds = new HashSet<>();
for (CanteenOnlineComboItemEntity item : items) {
if (item.getChildSpuId() == null) {
continue;
}
if (!childIds.add(item.getChildSpuId())) {
throw new IllegalArgumentException("套餐组成菜品不能重复");
}
if (selfId != null && Objects.equals(item.getChildSpuId(), selfId)) {
throw new IllegalArgumentException("套餐不能包含自身");
}
}
List<CanteenOnlineSpuEntity> children = baseMapper.selectBatchIds(childIds);
if (children.size() != childIds.size()) {
throw new IllegalArgumentException("套餐组成菜品不存在或已删除");
}
for (CanteenOnlineSpuEntity child : children) {
if (entity.getMerchantId() != null && !Objects.equals(child.getMerchantId(), entity.getMerchantId())) {
throw new IllegalArgumentException("套餐组成菜品必须属于同一商户:" + child.getName());
}
Integer childType = child.getSpuType() == null ? 1 : child.getSpuType();
if (Objects.equals(childType, 2)) {
throw new IllegalArgumentException("套餐组成不能再包含套餐:" + child.getName());
}
}
}
private void saveComboItems(Long comboSpuId, Integer spuType, List<CanteenOnlineComboItemEntity> items) {
if (!Objects.equals(spuType, 2) || CollUtil.isEmpty(items)) {
return;
}
int sort = 0;
LocalDateTime now = LocalDateTime.now();
for (CanteenOnlineComboItemEntity item : items) {
if (item.getChildSpuId() == null) {
continue;
}
CanteenOnlineComboItemEntity line = new CanteenOnlineComboItemEntity();
line.setComboSpuId(comboSpuId);
line.setChildSpuId(item.getChildSpuId());
line.setQuantity(item.getQuantity() == null || item.getQuantity() < 1 ? 1 : item.getQuantity());
line.setSortOrder(item.getSortOrder() != null ? item.getSortOrder() : sort++);
line.setCreateTime(now);
comboItemMapper.insert(line);
}
}
private Map<Long, List<CanteenOnlineComboItemEntity>> loadComboItems(List<Long> comboIds) {
if (CollUtil.isEmpty(comboIds)) {
return Collections.emptyMap();
}
List<CanteenOnlineComboItemEntity> lines = comboItemMapper.selectList(
Wrappers.<CanteenOnlineComboItemEntity>lambdaQuery()
.in(CanteenOnlineComboItemEntity::getComboSpuId, comboIds)
.orderByAsc(CanteenOnlineComboItemEntity::getSortOrder)
.orderByAsc(CanteenOnlineComboItemEntity::getId));
if (CollUtil.isEmpty(lines)) {
return Collections.emptyMap();
}
Set<Long> childIds = lines.stream().map(CanteenOnlineComboItemEntity::getChildSpuId)
.filter(Objects::nonNull).collect(Collectors.toSet());
Map<Long, CanteenOnlineSpuEntity> childMap = childIds.isEmpty() ? Collections.emptyMap()
: baseMapper.selectBatchIds(childIds).stream()
.collect(Collectors.toMap(CanteenOnlineSpuEntity::getId, c -> c, (a, b) -> a));
for (CanteenOnlineComboItemEntity line : lines) {
CanteenOnlineSpuEntity child = childMap.get(line.getChildSpuId());
if (child != null) {
line.setChildSpuName(child.getName());
line.setChildSpuPicUrl(child.getPicUrl());
line.setChildSpuPrice(child.getPrice());
}
}
return lines.stream().collect(Collectors.groupingBy(CanteenOnlineComboItemEntity::getComboSpuId,
LinkedHashMap::new, Collectors.toList()));
}
private List<OnlineSpuComboItemVO> toAppComboVos(List<CanteenOnlineComboItemEntity> lines) {
if (CollUtil.isEmpty(lines)) {
return Collections.emptyList();
}
List<OnlineSpuComboItemVO> list = new ArrayList<>(lines.size());
for (CanteenOnlineComboItemEntity line : lines) {
OnlineSpuComboItemVO vo = new OnlineSpuComboItemVO();
vo.setChildSpuId(line.getChildSpuId());
vo.setName(line.getChildSpuName());
vo.setPicUrl(line.getChildSpuPicUrl());
vo.setQuantity(line.getQuantity());
vo.setSortOrder(line.getSortOrder());
vo.setPrice(line.getChildSpuPrice());
list.add(vo);
}
return list;
}
@Override
public R<List<APPOnlineSpuVO>> getCanteenOnlineSpuList(String categoryId, String merchantId, String time,
Integer minPrice, Integer maxPrice, Integer status,
@@ -137,6 +353,7 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
bean.get(i).setSoldOut(st == null || st <= 0);
}
fillAppOnlineSpuChoiceGroups(bean, finallyCanteenSpuEntities);
fillAppComboItems(bean, finallyCanteenSpuEntities);
return R.ok(bean);
}
@@ -412,9 +629,26 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
LambdaQueryWrapper<CanteenOnlineSpuEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StrUtil.isNotBlank(canteenOnlineSpu.getName()), CanteenOnlineSpuEntity::getName, canteenOnlineSpu.getName())
.eq(Objects.nonNull(canteenOnlineSpu.getCategoryId()), CanteenOnlineSpuEntity::getCategoryId, canteenOnlineSpu.getCategoryId())
.eq(Objects.nonNull(canteenOnlineSpu.getMerchantId()), CanteenOnlineSpuEntity::getMerchantId, canteenOnlineSpu.getMerchantId())
// 套餐组成只可选单品(spu_type 为空或 1)
.and(w -> w.isNull(CanteenOnlineSpuEntity::getSpuType).or().eq(CanteenOnlineSpuEntity::getSpuType, 1))
.eq(CanteenOnlineSpuEntity::getStatus, 0)
.orderByAsc(CanteenOnlineSpuEntity::getSort).orderByDesc(CanteenOnlineSpuEntity::getId);
Page<CanteenOnlineSpuEntity> pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
List<CanteenOnlineSpuSimpleVO> voList = BeanUtils.toBean(pageData.getRecords(), CanteenOnlineSpuSimpleVO.class);
List<CanteenOnlineSpuSimpleVO> voList = new ArrayList<>();
if (CollUtil.isNotEmpty(pageData.getRecords())) {
for (CanteenOnlineSpuEntity record : pageData.getRecords()) {
CanteenOnlineSpuSimpleVO vo = new CanteenOnlineSpuSimpleVO();
vo.setId(record.getId());
vo.setName(record.getName());
vo.setPicUrl(record.getPicUrl());
vo.setPrice(record.getPrice());
vo.setLinkSpuId(record.getLinkSpuId());
vo.setRequiredChoice(record.getRequiredChoice());
vo.setChoiceGroupId(record.getChoiceGroupId());
voList.add(vo);
}
}
Page<CanteenOnlineSpuSimpleVO> resultPage = new Page<>();
resultPage.setRecords(voList);
resultPage.setCurrent(pageData.getCurrent());
@@ -432,6 +666,9 @@ public class CanteenOnlineSpuServiceImpl extends ServiceImpl<CanteenOnlineSpuMap
if (CollUtil.isNotEmpty(records)) {
// 为每条记录添加菜品类型标识
records.forEach(record -> {
if (record.getSpuType() == null) {
record.setSpuType(1);
}
if (record.getLinkSpuId() == null) {
record.setDishTypeName("常驻菜品");
record.setDishType(1);
@@ -13,6 +13,7 @@
<result property="sliderPicUrls" column="slider_pic_urls"/>
<result property="sort" column="sort"/>
<result property="status" column="status"/>
<result property="spuType" column="spu_type"/>
<result property="requiredChoice" column="required_choice"/>
<result property="choiceGroupId" column="choice_group_id"/>
<result property="price" column="price"/>
@@ -78,11 +78,6 @@ public class UserDTO extends SysUser implements BaseIsIncludeTreeInterface {
*/
private String nameOrPhone;
/**
* 用户id
*/
private Long userId;
private List<Long> userIds;
/**
@@ -444,6 +444,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
if (userId == null) {
throw new RuntimeException("用户ID不能为空");
}
SysUser dbUser = this.getById(userId);
if (dbUser == null) {
throw new RuntimeException("用户不存在或用户ID无效,请刷新后重试");
}
// 以库中主键为准,避免前端 Long 精度丢失后 ne 条件排除失败
userId = dbUser.getUserId();
userDto.setUserId(userId);
sysUserIdentityService.validateDto(userDto);
// 更新用户表信息
@@ -460,12 +466,12 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
// 脱敏手机号不参与更新,避免把 138****1234 写回库
if (StrUtil.isNotBlank(userDto.getPhone()) && userDto.getPhone().contains("*")) {
SysUser dbUser = this.getById(userId);
sysUser.setPhone(dbUser != null ? dbUser.getPhone() : null);
sysUser.setPhone(dbUser.getPhone());
}
// 判断用户username是否重复
if (existUsername(userId, userDto.getUsername())) {
// 用户名未变更则不校验;变更时排除自身
if (!StrUtil.equals(dbUser.getUsername(), userDto.getUsername())
&& existUsername(userId, userDto.getUsername())) {
throw new RuntimeException("该用户名已存在");
}
// 判断手机号是否重复
@@ -1214,48 +1220,41 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
return com.bsd.pjcloud.common.core.util.BeanUtils.toBean(sysUsers, SimpleUserListVO.class);
}
/**
* 用户名是否已被占用。
* 编辑时必须传入正确的 userId,用 ne 排除自身;若 userId 因前端 Long 精度丢失而不等于库中值,
* 会误把当前用户算作“他人占用”。调用方应先 getById 校验并回填真实主键。
*/
private boolean existUsername(Long userId, String username) {
Long count = baseMapper.selectCount(new LambdaQueryWrapper<SysUser>()
.eq(ObjectUtil.isNotEmpty(username), SysUser::getUsername, username)
.ne(ObjectUtil.isNotEmpty(userId), SysUser::getUserId, userId)
if (StrUtil.isBlank(username)) {
return false;
}
Long count = baseMapper.selectCount(Wrappers.<SysUser>lambdaQuery()
.eq(SysUser::getUsername, username.trim())
.ne(userId != null, SysUser::getUserId, userId)
);
if (ObjectUtil.isNotEmpty(userId)) {
return count >= 1;
}
return count > 0;
return count != null && count > 0;
}
private boolean existPhone(Long userId, String phone) {
if (StrUtil.isBlank(phone) || phone.contains("*")) {
return false;
}
Long count = baseMapper.selectCount(new LambdaQueryWrapper<SysUser>()
Long count = baseMapper.selectCount(Wrappers.<SysUser>lambdaQuery()
.eq(SysUser::getPhone, phone)
.ne(ObjectUtil.isNotEmpty(userId), SysUser::getUserId, userId)
.ne(userId != null, SysUser::getUserId, userId)
);
if (ObjectUtil.isNotEmpty(userId)) {
return count >= 1;
}
return count > 0;
return count != null && count > 0;
}
/**
* UserDTO 子类重复声明了 userId,Jackson 可能只写入父类字段,需兼容读取。
* 读取更新用的用户ID(兼容历史请求体)。
* 注意:UserDTO 曾重复声明 userId,可能导致 Jackson 只写入父类字段。
*/
private Long resolveUserIdForUpdate(UserDTO userDto) {
if (userDto.getUserId() != null) {
return userDto.getUserId();
}
try {
java.lang.reflect.Field field = SysUser.class.getDeclaredField("userId");
field.setAccessible(true);
Object value = field.get(userDto);
if (value instanceof Long) {
return (Long) value;
}
} catch (ReflectiveOperationException ignored) {
// ignore
}
return null;
}
}
@@ -32,6 +32,7 @@ public interface WalletManageMapper extends PjcloudBaseMapper<WalletManageEntity
.eq(ObjectUtil.isNotEmpty(dto.getWalletTypeId()), WalletManageEntity::getWalletTypeId,
dto.getWalletTypeId())
.eq(ObjectUtil.isNotEmpty(dto.getIsFreeze()), WalletManageEntity::getIsFreeze, dto.getIsFreeze())
.like(ObjectUtil.isNotEmpty(dto.getCardNumber()), WalletManageEntity::getCardNumber, dto.getCardNumber())
.in(ObjectUtil.isNotEmpty(dto.getWalletIds()), WalletManageEntity::getId, dto.getWalletIds())
.in(ObjectUtil.isNotEmpty(dto.getUserIds()), WalletManageEntity::getUserId, dto.getUserIds())
.between(