优化布局
This commit is contained in:
+14
@@ -152,6 +152,20 @@ public class ShopManageService {
|
|||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询匹配业务允许使用的后台店铺;不存在时返回统一中文业务错误。
|
||||||
|
*/
|
||||||
|
public ShopManageEntity requireShopByName(String shopName) {
|
||||||
|
String normalizedShopName = normalizeRequired(shopName, "店铺名称不能为空");
|
||||||
|
ShopManageEntity entity = shopManageMapper.selectOne(new LambdaQueryWrapper<ShopManageEntity>()
|
||||||
|
.eq(ShopManageEntity::getShopName, normalizedShopName)
|
||||||
|
.last("limit 1"));
|
||||||
|
if (entity == null) {
|
||||||
|
throw new BusinessException("后台店铺管理中未找到店铺:" + normalizedShopName + ",请先添加店铺信息");
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
public String findMallNameByShopName(String shopName) {
|
public String findMallNameByShopName(String shopName) {
|
||||||
String normalizedShopName = normalizeRequired(shopName, "shopName required");
|
String normalizedShopName = normalizeRequired(shopName, "shopName required");
|
||||||
ShopManageEntity entity = shopManageMapper.selectOne(new LambdaQueryWrapper<ShopManageEntity>()
|
ShopManageEntity entity = shopManageMapper.selectOne(new LambdaQueryWrapper<ShopManageEntity>()
|
||||||
|
|||||||
+17
-3
@@ -1,6 +1,7 @@
|
|||||||
package com.nanri.aiimage.modules.ziniao.service;
|
package com.nanri.aiimage.modules.ziniao.service;
|
||||||
|
|
||||||
import com.nanri.aiimage.common.exception.BusinessException;
|
import com.nanri.aiimage.common.exception.BusinessException;
|
||||||
|
import com.nanri.aiimage.modules.shopkey.service.ShopManageService;
|
||||||
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopMatchResultVo;
|
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopMatchResultVo;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -11,9 +12,10 @@ public class ZiniaoShopSwitchService {
|
|||||||
|
|
||||||
private final ZiniaoAuthService ziniaoAuthService;
|
private final ZiniaoAuthService ziniaoAuthService;
|
||||||
private final ZiniaoShopIndexService ziniaoShopIndexService;
|
private final ZiniaoShopIndexService ziniaoShopIndexService;
|
||||||
|
private final ShopManageService shopManageService;
|
||||||
|
|
||||||
public ZiniaoShopMatchResultVo matchStoreByNameAcrossStaff(String targetShopName, Long preferUserId) {
|
public ZiniaoShopMatchResultVo matchStoreByNameAcrossStaff(String targetShopName, Long preferUserId) {
|
||||||
String normalizedShopName = normalizeShopName(targetShopName);
|
String normalizedShopName = requireManagedShopName(targetShopName);
|
||||||
if (normalizedShopName.isBlank()) {
|
if (normalizedShopName.isBlank()) {
|
||||||
return emptyMatchResult();
|
return emptyMatchResult();
|
||||||
}
|
}
|
||||||
@@ -31,11 +33,15 @@ public class ZiniaoShopSwitchService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ZiniaoShopMatchResultVo findIndexedStoreByName(String targetShopName) {
|
public ZiniaoShopMatchResultVo findIndexedStoreByName(String targetShopName) {
|
||||||
return ziniaoShopIndexService.findIndexedStoreByName(targetShopName);
|
return findIndexedStoreByName(targetShopName, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZiniaoShopMatchResultVo findIndexedStoreByName(String targetShopName, boolean buildOpenStoreUrl) {
|
public ZiniaoShopMatchResultVo findIndexedStoreByName(String targetShopName, boolean buildOpenStoreUrl) {
|
||||||
return ziniaoShopIndexService.findIndexedStoreByName(targetShopName, buildOpenStoreUrl);
|
String normalizedShopName = requireManagedShopName(targetShopName);
|
||||||
|
if (normalizedShopName.isBlank()) {
|
||||||
|
return emptyMatchResult();
|
||||||
|
}
|
||||||
|
return ziniaoShopIndexService.findIndexedStoreByName(normalizedShopName, buildOpenStoreUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ZiniaoShopMatchResultVo emptyMatchResult() {
|
public ZiniaoShopMatchResultVo emptyMatchResult() {
|
||||||
@@ -53,6 +59,14 @@ public class ZiniaoShopSwitchService {
|
|||||||
return value.replace(" ", " ").trim();
|
return value.replace(" ", " ").trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String requireManagedShopName(String targetShopName) {
|
||||||
|
String normalizedShopName = normalizeShopName(targetShopName);
|
||||||
|
if (!normalizedShopName.isBlank()) {
|
||||||
|
shopManageService.requireShopByName(normalizedShopName);
|
||||||
|
}
|
||||||
|
return normalizedShopName;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSkippableMatchError(BusinessException ex) {
|
public boolean isSkippableMatchError(BusinessException ex) {
|
||||||
String message = ex == null ? null : ex.getMessage();
|
String message = ex == null ? null : ex.getMessage();
|
||||||
if (message == null || message.isBlank()) {
|
if (message == null || message.isBlank()) {
|
||||||
|
|||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
package com.nanri.aiimage.modules.shopkey.service;
|
||||||
|
|
||||||
|
import com.nanri.aiimage.common.exception.BusinessException;
|
||||||
|
import com.nanri.aiimage.common.security.ShopCredentialCryptoService;
|
||||||
|
import com.nanri.aiimage.modules.shopkey.mapper.ShopManageMapper;
|
||||||
|
import com.nanri.aiimage.modules.shopkey.model.entity.ShopManageEntity;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ShopManageServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ShopManageMapper shopManageMapper;
|
||||||
|
@Mock
|
||||||
|
private ShopManageGroupService shopManageGroupService;
|
||||||
|
@Mock
|
||||||
|
private ShopCredentialCryptoService shopCredentialCryptoService;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private ShopManageService service;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requireShopByNameReturnsManagedShop() {
|
||||||
|
ShopManageEntity entity = new ShopManageEntity();
|
||||||
|
entity.setShopName("测试店铺");
|
||||||
|
when(shopManageMapper.selectOne(any())).thenReturn(entity);
|
||||||
|
|
||||||
|
assertSame(entity, service.requireShopByName(" 测试店铺 "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void requireShopByNameReturnsChineseErrorWhenMissing() {
|
||||||
|
when(shopManageMapper.selectOne(any())).thenReturn(null);
|
||||||
|
|
||||||
|
BusinessException exception = assertThrows(BusinessException.class,
|
||||||
|
() -> service.requireShopByName("缺失店铺"));
|
||||||
|
|
||||||
|
assertEquals("后台店铺管理中未找到店铺:缺失店铺,请先添加店铺信息", exception.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
@@ -0,0 +1,83 @@
|
|||||||
|
package com.nanri.aiimage.modules.ziniao.service;
|
||||||
|
|
||||||
|
import com.nanri.aiimage.common.exception.BusinessException;
|
||||||
|
import com.nanri.aiimage.modules.shopkey.service.ShopManageService;
|
||||||
|
import com.nanri.aiimage.modules.ziniao.model.vo.ZiniaoShopMatchResultVo;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ZiniaoShopSwitchServiceTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ZiniaoAuthService ziniaoAuthService;
|
||||||
|
@Mock
|
||||||
|
private ZiniaoShopIndexService ziniaoShopIndexService;
|
||||||
|
@Mock
|
||||||
|
private ShopManageService shopManageService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingManagedShopStopsIndexedLookup() {
|
||||||
|
String message = "后台店铺管理中未找到店铺:缺失店铺,请先添加店铺信息";
|
||||||
|
when(shopManageService.requireShopByName("缺失店铺"))
|
||||||
|
.thenThrow(new BusinessException(message));
|
||||||
|
ZiniaoShopSwitchService service = new ZiniaoShopSwitchService(
|
||||||
|
ziniaoAuthService, ziniaoShopIndexService, shopManageService);
|
||||||
|
|
||||||
|
BusinessException exception = assertThrows(BusinessException.class,
|
||||||
|
() -> service.findIndexedStoreByName("缺失店铺", false));
|
||||||
|
|
||||||
|
assertEquals(message, exception.getMessage());
|
||||||
|
verifyNoInteractions(ziniaoShopIndexService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void managedShopIsCheckedBeforeIndexedLookup() {
|
||||||
|
ZiniaoShopMatchResultVo expected = new ZiniaoShopMatchResultVo();
|
||||||
|
expected.setMatched(true);
|
||||||
|
when(ziniaoShopIndexService.findIndexedStoreByName("测试店铺", false)).thenReturn(expected);
|
||||||
|
ZiniaoShopSwitchService service = new ZiniaoShopSwitchService(
|
||||||
|
ziniaoAuthService, ziniaoShopIndexService, shopManageService);
|
||||||
|
|
||||||
|
ZiniaoShopMatchResultVo actual = service.findIndexedStoreByName(" 测试店铺 ", false);
|
||||||
|
|
||||||
|
assertSame(expected, actual);
|
||||||
|
InOrder order = inOrder(shopManageService, ziniaoShopIndexService);
|
||||||
|
order.verify(shopManageService).requireShopByName("测试店铺");
|
||||||
|
order.verify(ziniaoShopIndexService).findIndexedStoreByName("测试店铺", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void missingManagedShopStopsDirectStaffMatch() {
|
||||||
|
when(shopManageService.requireShopByName("缺失店铺"))
|
||||||
|
.thenThrow(new BusinessException("后台店铺管理中未找到店铺:缺失店铺,请先添加店铺信息"));
|
||||||
|
ZiniaoShopSwitchService service = new ZiniaoShopSwitchService(
|
||||||
|
ziniaoAuthService, ziniaoShopIndexService, shopManageService);
|
||||||
|
|
||||||
|
assertThrows(BusinessException.class,
|
||||||
|
() -> service.matchStoreByNameAcrossStaff("缺失店铺", 12L));
|
||||||
|
verifyNoInteractions(ziniaoAuthService);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void blankShopNameDoesNotQueryManagedShopOrZiniao() {
|
||||||
|
ZiniaoShopSwitchService service = new ZiniaoShopSwitchService(
|
||||||
|
ziniaoAuthService, ziniaoShopIndexService, shopManageService);
|
||||||
|
|
||||||
|
ZiniaoShopMatchResultVo result = service.findIndexedStoreByName(" ", false);
|
||||||
|
|
||||||
|
assertEquals(ZiniaoShopIndexService.MATCH_STATUS_PENDING, result.getMatchStatus());
|
||||||
|
assertEquals("店铺名为空,无法匹配索引", result.getMatchMessage());
|
||||||
|
verifyNoInteractions(shopManageService, ziniaoShopIndexService, ziniaoAuthService);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -387,7 +387,10 @@ async function launchDesktop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.delivery-page {
|
.delivery-page {
|
||||||
min-height: 100vh;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
background: #0f0f0f;
|
background: #0f0f0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,7 +402,9 @@ async function launchDesktop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.delivery-page__body {
|
.delivery-page__body {
|
||||||
min-height: calc(100vh - 64px);
|
flex: 1 1 auto;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
padding: 12px 20px 24px;
|
padding: 12px 20px 24px;
|
||||||
background: #0f0f0f;
|
background: #0f0f0f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="ziniao-version-setting">
|
<section class="ziniao-version-setting">
|
||||||
<div class="setting-title">紫鸟版本</div>
|
<div class="setting-title">紫鸟版本</div>
|
||||||
<div class="version-options" role="radiogroup" aria-label="紫鸟版本">
|
<div class="version-options" role="group" aria-label="紫鸟版本">
|
||||||
<label v-for="option in options" :key="option.value" class="version-option"
|
<button v-for="option in options" :key="option.value" type="button" class="version-option"
|
||||||
:class="{ active: modelValue === option.value }">
|
:class="{ active: modelValue === option.value }" :aria-pressed="modelValue === option.value"
|
||||||
<input :checked="modelValue === option.value" type="radio" :name="radioName" :value="option.value"
|
@mousedown.prevent @click="emit('update:modelValue', option.value)">
|
||||||
@change="emit('update:modelValue', option.value)" />
|
|
||||||
<span>{{ option.label }}</span>
|
<span>{{ option.label }}</span>
|
||||||
</label>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
@@ -17,7 +16,6 @@ import type { ZiniaoVersion } from '@/shared/utils/ziniao-version'
|
|||||||
|
|
||||||
defineProps<{ modelValue: ZiniaoVersion }>()
|
defineProps<{ modelValue: ZiniaoVersion }>()
|
||||||
const emit = defineEmits<{ 'update:modelValue': [value: ZiniaoVersion] }>()
|
const emit = defineEmits<{ 'update:modelValue': [value: ZiniaoVersion] }>()
|
||||||
const radioName = `ziniao-version-${Math.random().toString(36).slice(2)}`
|
|
||||||
const options: Array<{ label: string; value: ZiniaoVersion }> = [
|
const options: Array<{ label: string; value: ZiniaoVersion }> = [
|
||||||
{ label: '新版', value: 'new' },
|
{ label: '新版', value: 'new' },
|
||||||
{ label: '旧版', value: 'old' },
|
{ label: '旧版', value: 'old' },
|
||||||
@@ -28,8 +26,7 @@ const options: Array<{ label: string; value: ZiniaoVersion }> = [
|
|||||||
.ziniao-version-setting { margin: 16px 0; }
|
.ziniao-version-setting { margin: 16px 0; }
|
||||||
.setting-title { margin-bottom: 10px; color: #bbb; font-size: 13px; }
|
.setting-title { margin-bottom: 10px; color: #bbb; font-size: 13px; }
|
||||||
.version-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; min-height: 40px; padding: 3px; border: 1px solid #3a3a3a; border-radius: 6px; background: #202020; }
|
.version-options { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); width: 100%; min-height: 40px; padding: 3px; border: 1px solid #3a3a3a; border-radius: 6px; background: #202020; }
|
||||||
.version-option { display: flex; min-width: 0; align-items: center; justify-content: center; border-radius: 4px; color: #aaa; cursor: pointer; font-size: 13px; transition: background-color .15s ease, color .15s ease; }
|
.version-option { display: flex; min-width: 0; align-items: center; justify-content: center; border: 0; border-radius: 4px; background: transparent; color: #aaa; cursor: pointer; font-size: 13px; transition: background-color .15s ease, color .15s ease; }
|
||||||
.version-option:hover { color: #eee; }
|
.version-option:hover { color: #eee; }
|
||||||
.version-option.active { background: #409eff; color: #fff; }
|
.version-option.active { background: #409eff; color: #fff; }
|
||||||
.version-option input { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
@import './reset.css';
|
@import './reset.css';
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: var(--color-bg-light);
|
background: #1a1a1a;
|
||||||
font-family: var(--font-family-base);
|
font-family: var(--font-family-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,35 +39,29 @@ html {
|
|||||||
|
|
||||||
.page-shell {
|
.page-shell {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
min-height: 100dvh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Match the desktop client shell in web_source/brand.html. */
|
||||||
* The desktop client uses a resizable WebView. Brand modules have a dynamic
|
|
||||||
* navigation header, so their content must consume the remaining space
|
|
||||||
* instead of subtracting the old fixed 56px header height.
|
|
||||||
*/
|
|
||||||
@media (min-width: 1101px) {
|
@media (min-width: 1101px) {
|
||||||
html:has(.module-page),
|
body {
|
||||||
body:has(.module-page) {
|
display: flex;
|
||||||
height: 100%;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #1a1a1a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#app:has(> .module-page) {
|
#app {
|
||||||
height: 100vh;
|
flex: 1 1 auto;
|
||||||
height: 100dvh;
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app > .page-shell {
|
||||||
|
height: 100%;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
|
||||||
background: #1a1a1a;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.module-page {
|
.module-page {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
|
||||||
height: 100dvh;
|
|
||||||
min-height: 0 !important;
|
min-height: 0 !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -78,8 +72,8 @@ html {
|
|||||||
|
|
||||||
.module-page > .main-content {
|
.module-page > .main-content {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
height: auto !important;
|
|
||||||
min-height: 0 !important;
|
min-height: 0 !important;
|
||||||
|
height: auto !important;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,15 +90,6 @@ html {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
|
||||||
body:has(.module-page),
|
|
||||||
#app:has(> .module-page) {
|
|
||||||
min-height: 100vh;
|
|
||||||
min-height: 100dvh;
|
|
||||||
background: #1a1a1a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-light {
|
.theme-light {
|
||||||
background: var(--color-bg-light);
|
background: var(--color-bg-light);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ html,
|
|||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
min-height: 100%;
|
height: 100%;
|
||||||
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
Reference in New Issue
Block a user