酒店
This commit is contained in:
1 parent
0c08d59457
commit
e6ead96354
12 files changed
+590
-24
No files matched your search
+60
@@ -0,0 +1,60 @@
|
||||
package com.bsd.pjcloud.supermarket.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.activerecord.Model;
|
||||
import com.bsd.pjcloud.common.core.util.TenantTable;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* App 出行/酒店入口可见部门白名单:配置部门及其子部门用户可见,未配置默认全员隐藏。
|
||||
*/
|
||||
@Data
|
||||
@TenantTable
|
||||
@TableName("sm_hotel_app_entry_dept")
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Schema(description = "App出行入口可见部门")
|
||||
public class SmHotelAppEntryDeptEntity extends Model<SmHotelAppEntryDeptEntity> {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
|
||||
private Long tenantId;
|
||||
|
||||
@Schema(description = "可见部门 ID(含其下子部门)")
|
||||
private Long deptId;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "1启用 0停用")
|
||||
private String status;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String createBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private String updateBy;
|
||||
|
||||
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
@TableLogic
|
||||
@TableField(fill = FieldFill.INSERT)
|
||||
private String delFlag;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "部门名称")
|
||||
private String deptName;
|
||||
}
|
||||
+9
@@ -22,6 +22,7 @@ import com.bsd.pjcloud.supermarket.mapper.SmHotelMapper;
|
||||
import com.bsd.pjcloud.supermarket.mapper.SmHotelProductMapper;
|
||||
import com.bsd.pjcloud.supermarket.mapper.SmHotelRoomMapper;
|
||||
import com.bsd.pjcloud.supermarket.mapper.SmHotelUserSearchPrefMapper;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelAppEntryService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelAvailabilityNotifyService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelMidService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelOrderService;
|
||||
@@ -124,8 +125,16 @@ public class SmHotelAppController {
|
||||
|
||||
private final SmHotelAvailabilityNotifyService availabilityNotifyService;
|
||||
|
||||
private final SmHotelAppEntryService appEntryService;
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
@Operation(summary = "当前用户是否显示出行Tab/订单出行入口(按部门白名单,默认隐藏)")
|
||||
@GetMapping("/entry-visible")
|
||||
public R<Map<String, Object>> entryVisible() {
|
||||
return R.ok(appEntryService.currentUserEntryVisible());
|
||||
}
|
||||
|
||||
@Operation(summary = "列表筛选项(区域/星级/设施)")
|
||||
@GetMapping("/filter-meta")
|
||||
public R<Map<String, Object>> filterMeta() {
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.bsd.pjcloud.supermarket.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.log.annotation.SysLog;
|
||||
import com.bsd.pjcloud.supermarket.entity.SmHotelAppEntryDeptEntity;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelAppEntryService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 管理端:App 出行 Tab / 订单出行入口按部门显隐。
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/smHotelAppEntry")
|
||||
@Tag(name = "酒店App出行入口配置")
|
||||
public class SmHotelAppEntryController {
|
||||
|
||||
private final SmHotelAppEntryService appEntryService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "分页")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_view')")
|
||||
public R<Page<SmHotelAppEntryDeptEntity>> page(@ParameterObject Page<SmHotelAppEntryDeptEntity> page,
|
||||
@ParameterObject SmHotelAppEntryDeptEntity query) {
|
||||
return R.ok(appEntryService.page(page, query));
|
||||
}
|
||||
|
||||
@GetMapping("/listEnabled")
|
||||
@Operation(summary = "启用中的可见部门")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_view')")
|
||||
public R<List<SmHotelAppEntryDeptEntity>> listEnabled() {
|
||||
return R.ok(appEntryService.listEnabled());
|
||||
}
|
||||
|
||||
@GetMapping("/enabledDeptIds")
|
||||
@Operation(summary = "启用部门 ID 列表(树勾选回显)")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_view')")
|
||||
public R<List<Long>> enabledDeptIds() {
|
||||
return R.ok(appEntryService.listEnabledDeptIds());
|
||||
}
|
||||
|
||||
@PostMapping("/replaceDepts")
|
||||
@SysLog("配置App出行入口可见部门")
|
||||
@Operation(summary = "整表替换可见部门(空=全员隐藏)")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_edit')")
|
||||
public R<Boolean> replaceDepts(@RequestBody Map<String, Object> body) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Object> raw = body == null ? null : (List<Object>) body.get("deptIds");
|
||||
List<Long> deptIds = new java.util.ArrayList<>();
|
||||
if (raw != null) {
|
||||
for (Object o : raw) {
|
||||
if (o == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
deptIds.add(Long.valueOf(String.valueOf(o)));
|
||||
}
|
||||
catch (Exception ignored) {
|
||||
// skip bad id
|
||||
}
|
||||
}
|
||||
}
|
||||
String remark = body == null || body.get("remark") == null ? null : String.valueOf(body.get("remark"));
|
||||
return appEntryService.replaceEnabledDepts(deptIds, remark);
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
@SysLog("新增App出行入口可见部门")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_edit')")
|
||||
public R<Boolean> save(@RequestBody SmHotelAppEntryDeptEntity entity) {
|
||||
return appEntryService.saveOne(entity);
|
||||
}
|
||||
|
||||
@PutMapping
|
||||
@SysLog("修改App出行入口可见部门")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_edit')")
|
||||
public R<Boolean> update(@RequestBody SmHotelAppEntryDeptEntity entity) {
|
||||
return appEntryService.updateOne(entity);
|
||||
}
|
||||
|
||||
@DeleteMapping
|
||||
@SysLog("删除App出行入口可见部门")
|
||||
@PreAuthorize("@pms.hasPermission('supermarket_smHotelAppEntry_edit')")
|
||||
public R<Boolean> remove(@RequestBody List<Long> ids) {
|
||||
return appEntryService.removeByIds(ids);
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package com.bsd.pjcloud.supermarket.mapper;
|
||||
|
||||
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
|
||||
import com.bsd.pjcloud.supermarket.entity.SmHotelAppEntryDeptEntity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SmHotelAppEntryDeptMapper extends PjcloudBaseMapper<SmHotelAppEntryDeptEntity> {
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package com.bsd.pjcloud.supermarket.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.supermarket.entity.SmHotelAppEntryDeptEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* App 出行/酒店入口按部门显隐。
|
||||
*/
|
||||
public interface SmHotelAppEntryService {
|
||||
|
||||
Page<SmHotelAppEntryDeptEntity> page(Page<SmHotelAppEntryDeptEntity> page, SmHotelAppEntryDeptEntity query);
|
||||
|
||||
List<SmHotelAppEntryDeptEntity> listEnabled();
|
||||
|
||||
/** 当前启用的可见部门 id 列表(管理端树勾选回显) */
|
||||
List<Long> listEnabledDeptIds();
|
||||
|
||||
/**
|
||||
* 整表替换启用部门:传入的 deptIds 启用,其余软删。
|
||||
* 空列表 = 全员隐藏。
|
||||
*/
|
||||
R<Boolean> replaceEnabledDepts(List<Long> deptIds, String remark);
|
||||
|
||||
R<Boolean> saveOne(SmHotelAppEntryDeptEntity entity);
|
||||
|
||||
R<Boolean> updateOne(SmHotelAppEntryDeptEntity entity);
|
||||
|
||||
R<Boolean> removeByIds(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 当前登录用户是否可见出行 Tab / 订单出行入口。
|
||||
* 未配置任何启用部门 → false;用户部门命中白名单或其子树 → true。
|
||||
*/
|
||||
boolean isTravelVisibleForCurrentUser();
|
||||
|
||||
Map<String, Object> currentUserEntryVisible();
|
||||
}
|
||||
+6
@@ -24,4 +24,10 @@ public interface SmHotelDownstreamAppService extends IService<SmHotelDownstreamA
|
||||
|
||||
/** 订单来源编码:优先 partnerCode,否则 appKey */
|
||||
String resolvePartnerCode(SmHotelDownstreamAppEntity app);
|
||||
|
||||
/**
|
||||
* 是否为启用中的开放应用(partnerCode 或 appKey 命中)。
|
||||
* 开放应用走拉模式,不走「渠道对接配置」推送。
|
||||
*/
|
||||
boolean isEnabledOpenAppPartner(String partnerCode);
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
package com.bsd.pjcloud.supermarket.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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.admin.api.entity.SysDept;
|
||||
import com.bsd.pjcloud.admin.api.feign.RemoteDeptService;
|
||||
import com.bsd.pjcloud.common.core.util.R;
|
||||
import com.bsd.pjcloud.common.security.service.PjcloudUser;
|
||||
import com.bsd.pjcloud.common.security.util.SecurityUtils;
|
||||
import com.bsd.pjcloud.supermarket.entity.SmHotelAppEntryDeptEntity;
|
||||
import com.bsd.pjcloud.supermarket.mapper.SmHotelAppEntryDeptMapper;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelAppEntryService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class SmHotelAppEntryServiceImpl extends ServiceImpl<SmHotelAppEntryDeptMapper, SmHotelAppEntryDeptEntity>
|
||||
implements SmHotelAppEntryService {
|
||||
|
||||
private final RemoteDeptService remoteDeptService;
|
||||
|
||||
@Override
|
||||
public Page<SmHotelAppEntryDeptEntity> page(Page<SmHotelAppEntryDeptEntity> page,
|
||||
SmHotelAppEntryDeptEntity query) {
|
||||
Page<SmHotelAppEntryDeptEntity> result = baseMapper.selectPage(page,
|
||||
Wrappers.<SmHotelAppEntryDeptEntity>lambdaQuery()
|
||||
.eq(query != null && query.getDeptId() != null, SmHotelAppEntryDeptEntity::getDeptId,
|
||||
query == null ? null : query.getDeptId())
|
||||
.eq(query != null && StrUtil.isNotBlank(query.getStatus()), SmHotelAppEntryDeptEntity::getStatus,
|
||||
query == null ? null : query.getStatus())
|
||||
.orderByDesc(SmHotelAppEntryDeptEntity::getUpdateTime)
|
||||
.orderByDesc(SmHotelAppEntryDeptEntity::getCreateTime));
|
||||
fillDeptNames(result.getRecords());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SmHotelAppEntryDeptEntity> listEnabled() {
|
||||
List<SmHotelAppEntryDeptEntity> list = baseMapper.selectList(Wrappers.<SmHotelAppEntryDeptEntity>lambdaQuery()
|
||||
.eq(SmHotelAppEntryDeptEntity::getStatus, "1")
|
||||
.orderByAsc(SmHotelAppEntryDeptEntity::getDeptId));
|
||||
fillDeptNames(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Long> listEnabledDeptIds() {
|
||||
return listEnabled().stream().map(SmHotelAppEntryDeptEntity::getDeptId).filter(Objects::nonNull).distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Boolean> replaceEnabledDepts(List<Long> deptIds, String remark) {
|
||||
Set<Long> want = new LinkedHashSet<>();
|
||||
if (CollUtil.isNotEmpty(deptIds)) {
|
||||
for (Long id : deptIds) {
|
||||
if (id != null && id > 0) {
|
||||
want.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
List<SmHotelAppEntryDeptEntity> existing = baseMapper.selectList(Wrappers.emptyWrapper());
|
||||
Map<Long, SmHotelAppEntryDeptEntity> byDept = new HashMap<>();
|
||||
for (SmHotelAppEntryDeptEntity row : existing) {
|
||||
if (row.getDeptId() != null) {
|
||||
byDept.put(row.getDeptId(), row);
|
||||
}
|
||||
}
|
||||
// 关掉不在本次勾选里的
|
||||
for (SmHotelAppEntryDeptEntity row : existing) {
|
||||
if (row.getDeptId() == null) {
|
||||
continue;
|
||||
}
|
||||
if (!want.contains(row.getDeptId())) {
|
||||
if (!"0".equals(row.getStatus())) {
|
||||
row.setStatus("0");
|
||||
baseMapper.updateById(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
String rm = StrUtil.trim(remark);
|
||||
for (Long deptId : want) {
|
||||
SmHotelAppEntryDeptEntity row = byDept.get(deptId);
|
||||
if (row == null) {
|
||||
row = new SmHotelAppEntryDeptEntity();
|
||||
row.setDeptId(deptId);
|
||||
row.setStatus("1");
|
||||
if (StrUtil.isNotBlank(rm)) {
|
||||
row.setRemark(rm);
|
||||
}
|
||||
baseMapper.insert(row);
|
||||
}
|
||||
else {
|
||||
boolean changed = false;
|
||||
if (!"1".equals(row.getStatus())) {
|
||||
row.setStatus("1");
|
||||
changed = true;
|
||||
}
|
||||
if (StrUtil.isNotBlank(rm) && !rm.equals(row.getRemark())) {
|
||||
row.setRemark(rm);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
baseMapper.updateById(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
return R.ok(Boolean.TRUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Boolean> saveOne(SmHotelAppEntryDeptEntity entity) {
|
||||
if (entity == null || entity.getDeptId() == null) {
|
||||
return R.failed("请选择部门");
|
||||
}
|
||||
Long cnt = baseMapper.selectCount(Wrappers.<SmHotelAppEntryDeptEntity>lambdaQuery()
|
||||
.eq(SmHotelAppEntryDeptEntity::getDeptId, entity.getDeptId())
|
||||
.eq(SmHotelAppEntryDeptEntity::getStatus, "1"));
|
||||
if (cnt != null && cnt > 0) {
|
||||
return R.failed("该部门已在可见列表中");
|
||||
}
|
||||
SmHotelAppEntryDeptEntity exist = baseMapper.selectOne(Wrappers.<SmHotelAppEntryDeptEntity>lambdaQuery()
|
||||
.eq(SmHotelAppEntryDeptEntity::getDeptId, entity.getDeptId())
|
||||
.last("limit 1"));
|
||||
if (exist != null) {
|
||||
exist.setStatus("1");
|
||||
if (StrUtil.isNotBlank(entity.getRemark())) {
|
||||
exist.setRemark(entity.getRemark());
|
||||
}
|
||||
return R.ok(baseMapper.updateById(exist) > 0);
|
||||
}
|
||||
if (StrUtil.isBlank(entity.getStatus())) {
|
||||
entity.setStatus("1");
|
||||
}
|
||||
return R.ok(baseMapper.insert(entity) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Boolean> updateOne(SmHotelAppEntryDeptEntity entity) {
|
||||
if (entity == null || entity.getId() == null) {
|
||||
return R.failed("缺少主键");
|
||||
}
|
||||
return R.ok(baseMapper.updateById(entity) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<Boolean> removeByIds(List<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return R.failed("请选择要删除的记录");
|
||||
}
|
||||
return R.ok(baseMapper.deleteBatchIds(ids) > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTravelVisibleForCurrentUser() {
|
||||
List<Long> whitelist = listEnabledDeptIds();
|
||||
if (CollUtil.isEmpty(whitelist)) {
|
||||
return false;
|
||||
}
|
||||
PjcloudUser user = SecurityUtils.getUser();
|
||||
if (user == null || user.getDeptId() == null) {
|
||||
return false;
|
||||
}
|
||||
Long userDeptId = user.getDeptId();
|
||||
Set<Long> allow = new HashSet<>(whitelist);
|
||||
if (allow.contains(userDeptId)) {
|
||||
return true;
|
||||
}
|
||||
// 用户部门链(含自身与上级):命中任一白名单部门即可见(白名单部门的子部门用户)
|
||||
try {
|
||||
R<List<SysDept>> r = remoteDeptService.getParentDeptList(userDeptId);
|
||||
if (r != null && r.isOk() && CollUtil.isNotEmpty(r.getData())) {
|
||||
for (SysDept d : r.getData()) {
|
||||
if (d != null && d.getDeptId() != null && allow.contains(d.getDeptId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("|出行入口|查部门链失败|deptId={}|{}", userDeptId, e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> currentUserEntryVisible() {
|
||||
boolean visible = isTravelVisibleForCurrentUser();
|
||||
Map<String, Object> data = new LinkedHashMap<>(4);
|
||||
data.put("travelVisible", visible);
|
||||
data.put("orderHotelVisible", visible);
|
||||
return data;
|
||||
}
|
||||
|
||||
private void fillDeptNames(List<SmHotelAppEntryDeptEntity> records) {
|
||||
if (CollUtil.isEmpty(records)) {
|
||||
return;
|
||||
}
|
||||
List<Long> deptIds = records.stream().map(SmHotelAppEntryDeptEntity::getDeptId).filter(Objects::nonNull)
|
||||
.distinct().collect(Collectors.toList());
|
||||
Map<Long, String> nameById = new HashMap<>();
|
||||
if (!deptIds.isEmpty()) {
|
||||
try {
|
||||
R<Map<Long, SysDept>> r = remoteDeptService.getDeptByDeptIds(deptIds);
|
||||
if (r != null && r.isOk() && r.getData() != null) {
|
||||
r.getData().forEach((id, d) -> {
|
||||
if (id != null && d != null) {
|
||||
nameById.put(id, d.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.warn("|出行入口|补全部门名失败|{}", e.getMessage());
|
||||
}
|
||||
}
|
||||
for (SmHotelAppEntryDeptEntity row : records) {
|
||||
if (row.getDeptId() != null) {
|
||||
row.setDeptName(nameById.getOrDefault(row.getDeptId(), String.valueOf(row.getDeptId())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -171,6 +171,9 @@ public class SmHotelChannelDeptServiceImpl extends ServiceImpl<SmHotelChannelDep
|
||||
return partners.stream().filter(StrUtil::isNotBlank).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 同一下游伙伴全部启用规则取并集:指定酒店 ∪ 各部门(+区域)命中的上架酒店。
|
||||
*/
|
||||
@Override
|
||||
public List<Long> listHotelIdsForDownstreamPartner(String partnerCode) {
|
||||
String partner = normalizeCode(partnerCode);
|
||||
@@ -180,13 +183,15 @@ public class SmHotelChannelDeptServiceImpl extends ServiceImpl<SmHotelChannelDep
|
||||
List<SmHotelChannelDeptEntity> scopes = baseMapper.selectList(Wrappers.<SmHotelChannelDeptEntity>lambdaQuery()
|
||||
.eq(SmHotelChannelDeptEntity::getDirection, SmHotelChannelDirection.DOWNSTREAM)
|
||||
.eq(SmHotelChannelDeptEntity::getStatus, "1")
|
||||
.eq(SmHotelChannelDeptEntity::getChannel, partner));
|
||||
// 忽略历史大小写差异,避免漏掉同伙伴另一条规则
|
||||
.apply("lower(channel) = {0}", partner));
|
||||
if (CollUtil.isEmpty(scopes)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Set<Long> ids = new LinkedHashSet<>();
|
||||
for (SmHotelChannelDeptEntity scope : scopes) {
|
||||
String scopeType = SmHotelDownstreamScopeType.normalize(scope.getScopeType());
|
||||
// 指定酒店:并入名单
|
||||
if (SmHotelDownstreamScopeType.isHotel(scopeType)) {
|
||||
if (StrUtil.isBlank(scope.getHotelIds())) {
|
||||
continue;
|
||||
@@ -199,6 +204,7 @@ public class SmHotelChannelDeptServiceImpl extends ServiceImpl<SmHotelChannelDep
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 按部门(+可选区域):并入该部门上架酒店
|
||||
if (scope.getDeptId() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
+19
@@ -1,5 +1,6 @@
|
||||
package com.bsd.pjcloud.supermarket.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
@@ -153,6 +154,24 @@ public class SmHotelDownstreamAppServiceImpl extends ServiceImpl<SmHotelDownstre
|
||||
return StrUtil.blankToDefault(StrUtil.trim(app.getPartnerCode()), StrUtil.trim(app.getAppKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabledOpenAppPartner(String partnerCode) {
|
||||
String code = StrUtil.trim(partnerCode);
|
||||
if (StrUtil.isBlank(code)) {
|
||||
return false;
|
||||
}
|
||||
List<SmHotelDownstreamAppEntity> apps = baseMapper.selectList(Wrappers.<SmHotelDownstreamAppEntity>lambdaQuery()
|
||||
.eq(SmHotelDownstreamAppEntity::getStatus, "1")
|
||||
.select(SmHotelDownstreamAppEntity::getId, SmHotelDownstreamAppEntity::getPartnerCode,
|
||||
SmHotelDownstreamAppEntity::getAppKey)
|
||||
.last("limit 500"));
|
||||
if (CollUtil.isEmpty(apps)) {
|
||||
return false;
|
||||
}
|
||||
return apps.stream().anyMatch(a -> code.equalsIgnoreCase(StrUtil.trim(a.getPartnerCode()))
|
||||
|| code.equalsIgnoreCase(StrUtil.trim(a.getAppKey())));
|
||||
}
|
||||
|
||||
private boolean existsAppKey(String appKey, Long excludeId) {
|
||||
Long cnt = baseMapper.selectCount(Wrappers.<SmHotelDownstreamAppEntity>lambdaQuery()
|
||||
.eq(SmHotelDownstreamAppEntity::getAppKey, appKey)
|
||||
|
||||
+8
-1
@@ -265,6 +265,7 @@ public class SmHotelOpenApiServiceImpl implements SmHotelOpenApiService {
|
||||
row.put("mealType", p.getMealType());
|
||||
row.put("mealCount", p.getMealCount());
|
||||
row.put("sellPrice", p.getSellPrice());
|
||||
row.put("stock", p.getStock() == null ? 0 : p.getStock());
|
||||
productList.add(row);
|
||||
}
|
||||
data.put("products", productList);
|
||||
@@ -307,9 +308,15 @@ public class SmHotelOpenApiServiceImpl implements SmHotelOpenApiService {
|
||||
row.put("productCode", d.getProductCode());
|
||||
row.put("stayDate", d.getStayDate() == null ? null : d.getStayDate().toString());
|
||||
row.put("sellPrice", d.getSellPrice());
|
||||
row.put("costPrice", d.getCostPrice());
|
||||
row.put("stock", d.getStock());
|
||||
row.put("sold", d.getSold());
|
||||
row.put("remain", d.getRemainStock());
|
||||
// remainStock 非表字段,开放接口按 stock-sold 计算,避免下游永远拿不到剩余
|
||||
int stock = d.getStock() == null ? 0 : d.getStock();
|
||||
int sold = d.getSold() == null ? 0 : d.getSold();
|
||||
int remain = Math.max(0, stock - sold);
|
||||
row.put("remain", remain);
|
||||
row.put("remainStock", remain);
|
||||
row.put("dailyStatus", d.getDailyStatus());
|
||||
list.add(row);
|
||||
}
|
||||
|
||||
+76
-21
@@ -1843,7 +1843,7 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
int need = rooms < 1 ? 1 : rooms;
|
||||
List<LocalDate> missing = new ArrayList<>();
|
||||
List<LocalDate> closed = new ArrayList<>();
|
||||
List<String> shortStock = new ArrayList<>();
|
||||
List<LocalDate> shortStock = new ArrayList<>();
|
||||
for (LocalDate d : SmHotelStayDates.occupyDates(checkIn, checkOut)) {
|
||||
SmHotelDailyEntity row = dailyMapper.selectOne(Wrappers.<SmHotelDailyEntity>lambdaQuery()
|
||||
.eq(SmHotelDailyEntity::getProductId, productId)
|
||||
@@ -1860,29 +1860,40 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
int stock = row.getStock() == null ? 0 : row.getStock();
|
||||
int sold = row.getSold() == null ? 0 : row.getSold();
|
||||
if (stock - sold < need) {
|
||||
shortStock.add(d + " 可售" + Math.max(0, stock - sold));
|
||||
shortStock.add(d);
|
||||
}
|
||||
}
|
||||
if (missing.isEmpty() && closed.isEmpty() && shortStock.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("所选入住区间不可订");
|
||||
if (!missing.isEmpty()) {
|
||||
sb.append(":缺少日历 ").append(formatDateList(missing));
|
||||
// C 端可读文案:避免「可售0 / 缺少日历」等运营术语
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!shortStock.isEmpty() || !closed.isEmpty()) {
|
||||
sb.append("你选择的日期房间已订满或暂不可订");
|
||||
List<String> badDays = new ArrayList<>();
|
||||
for (LocalDate d : closed) {
|
||||
badDays.add(formatStayDayCn(d));
|
||||
}
|
||||
for (LocalDate d : shortStock) {
|
||||
badDays.add(formatStayDayCn(d));
|
||||
}
|
||||
if (!badDays.isEmpty()) {
|
||||
int n = Math.min(badDays.size(), 5);
|
||||
sb.append("(").append(String.join("、", badDays.subList(0, n))).append(")");
|
||||
}
|
||||
}
|
||||
if (!closed.isEmpty()) {
|
||||
sb.append(missing.isEmpty() ? ":" : ";").append("已关房 ").append(formatDateList(closed));
|
||||
else if (!missing.isEmpty()) {
|
||||
sb.append("你选择的日期暂无房价,请更换日期");
|
||||
}
|
||||
if (!shortStock.isEmpty()) {
|
||||
sb.append(missing.isEmpty() && closed.isEmpty() ? ":" : ";")
|
||||
.append("库存不足 ").append(String.join("、", shortStock));
|
||||
else {
|
||||
sb.append("当前日期暂不可预订");
|
||||
}
|
||||
String hint = buildAvailableDatesHint(productId, need);
|
||||
if (StrUtil.isNotBlank(hint)) {
|
||||
sb.append("。").append(hint);
|
||||
}
|
||||
else {
|
||||
sb.append("。请更换入住日期或联系商家维护房价房量");
|
||||
sb.append("。请更换入住日期后再试");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -1913,9 +1924,52 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
}
|
||||
}
|
||||
if (ok.isEmpty()) {
|
||||
return "该房型近60天暂无可住日期";
|
||||
return "近两月暂无空房,可稍后再看或换其他房型";
|
||||
}
|
||||
return "近期可住:" + formatDateRanges(ok);
|
||||
return "建议改订:" + formatDateRangesCn(ok);
|
||||
}
|
||||
|
||||
private static String formatStayDayCn(LocalDate d) {
|
||||
if (d == null) {
|
||||
return "";
|
||||
}
|
||||
return d.getMonthValue() + "月" + d.getDayOfMonth() + "日";
|
||||
}
|
||||
|
||||
/** 近期可住区间,中文月日展示,如 11月3日至11日、11月15日 */
|
||||
private static String formatDateRangesCn(List<LocalDate> dates) {
|
||||
if (dates == null || dates.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
List<String> parts = new ArrayList<>();
|
||||
LocalDate start = dates.get(0);
|
||||
LocalDate prev = start;
|
||||
for (int i = 1; i < dates.size(); i++) {
|
||||
LocalDate cur = dates.get(i);
|
||||
if (prev.plusDays(1).equals(cur)) {
|
||||
prev = cur;
|
||||
continue;
|
||||
}
|
||||
parts.add(formatRangePartCn(start, prev));
|
||||
if (parts.size() >= 4) {
|
||||
parts.add("等");
|
||||
return String.join("、", parts);
|
||||
}
|
||||
start = cur;
|
||||
prev = cur;
|
||||
}
|
||||
parts.add(formatRangePartCn(start, prev));
|
||||
return String.join("、", parts);
|
||||
}
|
||||
|
||||
private static String formatRangePartCn(LocalDate start, LocalDate end) {
|
||||
if (start.equals(end)) {
|
||||
return formatStayDayCn(start);
|
||||
}
|
||||
if (start.getYear() == end.getYear() && start.getMonth() == end.getMonth()) {
|
||||
return formatStayDayCn(start) + "至" + end.getDayOfMonth() + "日";
|
||||
}
|
||||
return formatStayDayCn(start) + "至" + formatStayDayCn(end);
|
||||
}
|
||||
|
||||
private static String formatDateList(List<LocalDate> dates) {
|
||||
@@ -2331,15 +2385,16 @@ public class SmHotelOrderServiceImpl extends ServiceImpl<SmHotelOrderMapper, SmH
|
||||
for (LocalDate d : SmHotelStayDates.occupyDates(checkIn, checkOut)) {
|
||||
int updated = dailyMapper.increaseSoldIfEnough(productId, d, rooms);
|
||||
if (updated == 0) {
|
||||
SmHotelDailyEntity row = dailyMapper.selectOne(Wrappers.<SmHotelDailyEntity>lambdaQuery()
|
||||
.eq(SmHotelDailyEntity::getProductId, productId)
|
||||
.eq(SmHotelDailyEntity::getStayDate, d)
|
||||
.last("limit 1"));
|
||||
int stock = row == null || row.getStock() == null ? 0 : row.getStock();
|
||||
int sold = row == null || row.getSold() == null ? 0 : row.getSold();
|
||||
String hint = buildAvailableDatesHint(productId, rooms);
|
||||
return "所选入住区间不可订:日期 " + d + " 可售 " + Math.max(0, stock - sold) + ",需要 " + rooms
|
||||
+ (StrUtil.isNotBlank(hint) ? "。" + hint : "");
|
||||
StringBuilder race = new StringBuilder("你选择的日期房间刚被订完(")
|
||||
.append(formatStayDayCn(d)).append(")");
|
||||
if (StrUtil.isNotBlank(hint)) {
|
||||
race.append("。").append(hint);
|
||||
}
|
||||
else {
|
||||
race.append("。请更换入住日期后再试");
|
||||
}
|
||||
return race.toString();
|
||||
}
|
||||
}
|
||||
// 占房成功:下游(环启)需同步剩余可售,避免仍按原配额售卖
|
||||
|
||||
+9
-1
@@ -21,6 +21,7 @@ import com.bsd.pjcloud.supermarket.mapper.SmHotelMapper;
|
||||
import com.bsd.pjcloud.supermarket.mapper.SmHotelSyncLogMapper;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelChannelConfigService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelChannelDeptService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelDownstreamAppService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelDownstreamPushLogService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelMidService;
|
||||
import com.bsd.pjcloud.supermarket.service.SmHotelSyncService;
|
||||
@@ -104,6 +105,8 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
|
||||
private final SmHotelChannelDeptService channelDeptService;
|
||||
|
||||
private final SmHotelDownstreamAppService downstreamAppService;
|
||||
|
||||
private final SmHotelMapper hotelMapper;
|
||||
|
||||
private final SmHotelSyncLogMapper syncLogMapper;
|
||||
@@ -289,7 +292,12 @@ public class SmHotelSyncServiceImpl implements SmHotelSyncService {
|
||||
return R.failed("请选择下游伙伴");
|
||||
}
|
||||
if (!channelConfigService.listEnabledDownstreamPartners().contains(partner)) {
|
||||
return R.failed("下游伙伴「" + partner + "」未启用或不存在,请先到「渠道对接配置」维护");
|
||||
// 开放应用是对方主动拉数,不走协议推送;误点「全量推送」时给出明确说明
|
||||
if (downstreamAppService.isEnabledOpenAppPartner(partner)) {
|
||||
return R.failed("「" + partner + "」是开放应用(拉模式),无需也不支持「全量推送」。"
|
||||
+ "请在「渠道范围映射」配好授权即可,对方用 appKey 调开放 API 自行拉酒店/下单");
|
||||
}
|
||||
return R.failed("下游伙伴「" + partner + "」未启用或不存在,请先到「渠道对接配置」维护(圣诺/慧订/环启等协议下游)");
|
||||
}
|
||||
Long running = syncLogMapper.selectCount(Wrappers.<SmHotelSyncLogEntity>lambdaQuery()
|
||||
.eq(SmHotelSyncLogEntity::getSyncType, TYPE_DOWNSTREAM_PUSH)
|
||||
|
||||
Reference in new issue
Block a user