修改积分商城商品显示问题1 #35

Closed
free wants to merge 1 commits from sjy-one into master
520 changed files with 8513 additions and 8342 deletions
Showing only changes of commit 8d065ac88a - Show all commits

View File

@ -3,7 +3,6 @@
"baseUrl": "http://127.0.0.1:48080/admin-api",
"token": "test1",
"adminTenentId": "1",
"appApi": "http://127.0.0.1:48080/app-api",
"appToken": "test247",
"appTenentId": "1"
@ -12,7 +11,6 @@
"baseUrl": "http://127.0.0.1:8888/admin-api",
"token": "test1",
"adminTenentId": "1",
"appApi": "http://127.0.0.1:8888/app-api",
"appToken": "test1",
"appTenantId": "1"

View File

@ -15,6 +15,7 @@ import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import liquibase.CatalogAndSchema;
import liquibase.Scope;
import liquibase.database.AbstractJdbcDatabase;

View File

@ -133,7 +133,9 @@ public abstract class AbstractEngineConfiguration {
protected final Logger logger = LoggerFactory.getLogger(getClass());
/** The tenant id indicating 'no tenant' */
/**
* The tenant id indicating 'no tenant'
*/
public static final String NO_TENANT_ID = "";
/**
@ -261,9 +263,9 @@ public abstract class AbstractEngineConfiguration {
/**
* Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all.
*
* <p>
* If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema.
*
* <p>
* If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is
* correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used.
*/
@ -287,7 +289,7 @@ public abstract class AbstractEngineConfiguration {
/**
* Escape character for doing wildcard searches.
*
* <p>
* This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\';
*/
protected String databaseWildcardEscapeCharacter;

View File

@ -11,6 +11,7 @@ export interface DiscountActivityVO {
endTime?: Date
products?: DiscountProductVO[]
}
// 限时折扣相关 属性
export interface DiscountProductVO {
spuId: number

View File

@ -4,6 +4,7 @@ import request from '@/config/axios'
export interface PayWalletUserReqVO {
userId: number
}
/** 钱包 VO */
export interface WalletVO {
id: number

View File

@ -1,5 +1,6 @@
import {ElMessage, ElMessageBox, ElNotification} from 'element-plus'
import {useI18n} from './useI18n'
export const useMessage = () => {
const {t} = useI18n()
return {

View File

@ -7,6 +7,7 @@ import type { TableProps } from '@/components/Table/src/types'
import {TableSetPropsType} from '@/types/table'
const {t} = useI18n()
interface ResponseType<T = any> {
list: T[]
total?: number

View File

@ -1,7 +1,8 @@
const domSymbol = Symbol('watermark-dom')
export function useWatermark(appendEl: HTMLElement | null = document.body) {
let func: Fn = () => {}
let func: Fn = () => {
}
const id = domSymbol.toString()
const clear = () => {
const domId = document.getElementById(id)

View File

@ -3,6 +3,7 @@ import { store } from '../index'
// @ts-ignore
import {DictDataVO} from '@/api/system/dict/types'
import {CACHE_KEY, useCache} from '@/hooks/web/useCache'
const {wsCache} = useCache('sessionStorage')
import {getSimpleDictDataList} from '@/api/system/dict/dict.data'
@ -12,10 +13,12 @@ export interface DictValueType {
clorType?: string
cssClass?: string
}
export interface DictTypeType {
dictType: string
dictValue: DictValueType[]
}
export interface DictState {
dictMap: Map<string, any>
isSetDict: boolean

View File

@ -11,6 +11,7 @@ const elLocaleMap = {
'zh-CN': zhCn,
en: en
}
interface LocaleState {
currentLocale: LocaleDropdownType
localeMap: LocaleDropdownType[]

View File

@ -1,4 +1,5 @@
import {ElementPlusSize} from './elementPlus'
export interface ConfigGlobalTypes {
size?: ElementPlusSize
}

View File

@ -2,7 +2,8 @@ const isArray = function (obj: any): boolean {
return Object.prototype.toString.call(obj) === '[object Array]'
}
const Logger = () => {}
const Logger = () => {
}
Logger.typeColor = function (type: string) {
let color = ''

View File

@ -1,4 +1,5 @@
import {isServer} from './is'
const ieVersion = isServer ? 0 : Number((document as any).documentMode)
const SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g
const MOZ_HACK_REGEXP = /^moz([A-Z])/

View File

@ -52,8 +52,12 @@
</div>
<div class="button-wrapper" v-show="hoverConversationId === conversation.id">
<el-button class="btn" link @click.stop="handleTop(conversation)">
<el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon>
<el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon>
<el-icon title="置顶" v-if="!conversation.pinned">
<Top/>
</el-icon>
<el-icon title="置顶" v-if="conversation.pinned">
<Bottom/>
</el-icon>
</el-button>
<el-button class="btn" link @click.stop="updateConversationTitle(conversation)">
<el-icon title="编辑">
@ -285,7 +289,8 @@ const deleteChatConversation = async (conversation: ChatConversationVO) => {
await getChatConversationList()
//
emits('onConversationDelete', conversation)
} catch {}
} catch {
}
}
/** 清空对话 */
@ -303,7 +308,8 @@ const handleClearConversation = async () => {
await getChatConversationList()
//
emits('onConversationClear')
} catch {}
} catch {
}
}
/** 对话置顶 */

View File

@ -43,10 +43,14 @@
<img class="btn-image h-17px mr-12px" src="@/assets/ai/delete.svg"/>
</el-button>
<el-button class="btn-cus" link @click="onRefresh(item)">
<el-icon size="17"><RefreshRight /></el-icon>
<el-icon size="17">
<RefreshRight/>
</el-icon>
</el-button>
<el-button class="btn-cus" link @click="onEdit(item)">
<el-icon size="17"><Edit /></el-icon>
<el-icon size="17">
<Edit/>
</el-icon>
</el-button>
</div>
</div>
@ -182,6 +186,7 @@ onMounted(async () => {
flex-direction: column;
overflow-y: hidden;
padding: 0 20px;
.message-item {
margin-top: 50px;
}

View File

@ -13,10 +13,12 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="['edit', role]">
<Icon icon="ep:edit" color="#787878" />编辑
<Icon icon="ep:edit" color="#787878"/>
编辑
</el-dropdown-item>
<el-dropdown-item :command="['delete', role]" style="color: red">
<Icon icon="ep:delete" color="red" />删除
<Icon icon="ep:delete" color="red"/>
删除
</el-dropdown-item>
</el-dropdown-menu>
</template>

View File

@ -227,6 +227,7 @@ onMounted(async () => {
height: 100%;
overflow: hidden;
}
.el-tabs__nav-scroll {
margin: 10px 20px;
}

View File

@ -307,7 +307,8 @@ const handlerMessageClear = async () => {
await ChatMessageApi.deleteByConversationId(activeConversationId.value)
// message
activeMessageList.value = []
} catch {}
} catch {
}
}
/** 回到 message 列表的顶部 */
@ -468,7 +469,8 @@ const doSendMessageStream = async (userMessage: ChatMessageVO) => {
stopStream()
}
)
} catch {}
} catch {
}
}
/** 停止 stream 流式调用 */
@ -553,7 +555,8 @@ const textRoll = async () => {
}
}
let timer = setTimeout(task, textSpeed.value)
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -44,8 +44,14 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
@ -151,7 +157,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -44,8 +44,14 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
@ -163,7 +169,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -190,6 +190,7 @@ onUnmounted(async () => {
width: 100%;
height: 100%;
}
.task-card {
margin: 0;
padding: 0;
@ -212,6 +213,7 @@ onUnmounted(async () => {
margin-right: 20px;
margin-bottom: 20px;
}
> div:last-of-type {
//margin-bottom: 100px;
}

View File

@ -68,7 +68,8 @@ const platformOptions = [
]
/** 绘画 start */
const handleDrawStart = async (platform: string) => {}
const handleDrawStart = async (platform: string) => {
}
/** 绘画 complete */
const handleDrawComplete = async (platform: string) => {

View File

@ -75,8 +75,14 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
@ -222,7 +228,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 修改是否发布 */

View File

@ -43,15 +43,22 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['ai:api-key:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</el-form-item>
</el-form>
@ -170,7 +177,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -36,15 +36,22 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['ai:chat-model:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</el-form-item>
</el-form>
@ -173,7 +180,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -42,15 +42,22 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['ai:chat-role:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</el-form-item>
</el-form>
@ -177,7 +184,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 初始化 **/

View File

@ -1,8 +1,10 @@
<template>
<div class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">
<div
class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none">
<!-- 歌曲信息 -->
<div class="flex gap-[10px]">
<el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" class="w-[45px]"/>
<el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
class="w-[45px]"/>
<div>
<div>{{currentSong.name}}</div>
<div class="text-[12px] text-gray-400">{{currentSong.singer}}</div>
@ -12,7 +14,8 @@
<!-- 音频controls -->
<div class="flex gap-[12px] items-center">
<Icon icon="majesticons:back-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'" :size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/>
<Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'"
:size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/>
<Icon icon="majesticons:next-circle" :size="20" class="text-gray-300 cursor-pointer"/>
<div class="flex gap-[16px] items-center">
<span>{{audioProps.currentTime}}</span>
@ -20,14 +23,16 @@
<span>{{ audioProps.duration }}</span>
</div>
<!-- 音频 -->
<audio v-bind="audioProps" ref="audioRef" controls v-show="!audioProps" @timeupdate="audioTimeUpdate">
<audio v-bind="audioProps" ref="audioRef" controls v-show="!audioProps"
@timeupdate="audioTimeUpdate">
<source :src="audioUrl"/>
</audio>
</div>
<!-- 音量控制器 -->
<div class="flex gap-[16px] items-center">
<Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20" class="cursor-pointer" @click="toggleStatus('muted')"/>
<Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20"
class="cursor-pointer" @click="toggleStatus('muted')"/>
<el-slider v-model="audioProps.volume" color="#409eff" class="w-[160px!important] "/>
</div>
</div>

View File

@ -100,6 +100,7 @@ defineExpose({
:deep(.el-tabs) {
display: flex;
flex-direction: column;
.el-tabs__content {
padding: 0 7px;
overflow: auto;

View File

@ -2,8 +2,11 @@
<div class="flex bg-[var(--el-bg-color-overlay)] p-12px mb-12px rounded-1">
<div class="relative" @click="playSong">
<el-image :src="songInfo.imageUrl" class="flex-none w-80px"/>
<div class="bg-black bg-op-40 absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer">
<Icon :icon="currentSong.id === songInfo.id ? 'solar:pause-circle-bold':'mdi:arrow-right-drop-circle'" :size="30" />
<div
class="bg-black bg-op-40 absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer">
<Icon
:icon="currentSong.id === songInfo.id ? 'solar:pause-circle-bold':'mdi:arrow-right-drop-circle'"
:size="30"/>
</div>
</div>
<div class="ml-8px">

View File

@ -64,6 +64,7 @@
<script lang="ts" setup>
import Title from '../title/index.vue'
defineOptions({name: 'Lyric'})
const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop']

View File

@ -89,8 +89,14 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
@ -263,7 +269,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 修改是否发布 */

View File

@ -1,6 +1,7 @@
import { isEmpty } from '@/utils/is'
const emojiList = [
{ name: '[狗子]', file: 'gouzi.png' },
{ name: '[笑掉牙]', file: 'xiaodiaoya.png' },
{ name: '[可爱]', file: 'keai.png' },
{ name: '[冷酷]', file: 'lengku.png' },

View File

@ -20,8 +20,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
WEEK(2, ""),
MONTH(3, ""),
QUARTER(4, "季度"),
YEAR(5, "")
;
YEAR(5, "");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();

View File

@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.common.enums;
/**
* Web 过滤器顺序的枚举类保证过滤器按照符合我们的预期
*
* <p>
* 考虑到每个 starter 都需要用到该工具类所以放到 common 模块下的 enums 包下
*
* @author 芋道源码

View File

@ -6,10 +6,10 @@ import lombok.Data;
/**
* 错误码对象
*
* <p>
* 全局错误码占用 [0, 999], 参见 {@link GlobalErrorCodeConstants}
* 业务异常错误码占用 [1 000 000 000, +)参见 {@link ServiceErrorCodeRange}
*
* <p>
* TODO 错误码设计成对象的原因为未来的 i18 国际化做准备
*/
@Data

View File

@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
/**
* 全局错误码枚举
* 0-999 系统异常编码保留
*
* <p>
* 一般情况下使用 HTTP 响应状态码 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
* 虽然说HTTP 响应状态码作为业务使用表达能力偏弱但是使用在系统层面还是非常不错的
* 比较特殊的是因为之前一直使用 0 作为成功就不使用 200

View File

@ -2,9 +2,9 @@ package cn.iocoder.yudao.framework.common.exception.enums;
/**
* 业务异常的错误码区间解决解决各模块错误码定义避免重复在此只声明不做实际使用
*
* <p>
* 一共 10 分成四段
*
* <p>
* 第一段1 类型
* 1 - 业务级别异常
* x - 预留

View File

@ -8,10 +8,9 @@ import lombok.extern.slf4j.Slf4j;
/**
* {@link ServiceException} 工具类
*
* <p>
* 目的在于格式化异常信息提示
* 考虑到 String.format 在参数不正确时会报错因此使用 {} 作为占位符并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
*
*/
@Slf4j
public class ServiceExceptionUtil {

View File

@ -1,6 +1,6 @@
/**
* 基础的通用类和框架无关
*
* <p>
* 例如说CommonResult 为通用返回
*/
package cn.iocoder.yudao.framework.common;

View File

@ -37,7 +37,7 @@ public class CommonResult<T> implements Serializable {
/**
* 将传入的 result 对象转换成另外一个泛型结果的对象
*
* <p>
* 因为 A 方法返回的 CommonResult 对象不满足调用其的 B 方法的返回所以需要进行转换
*
* @param result 传入的 result 对象

View File

@ -17,7 +17,7 @@ public class PageParam implements Serializable {
/**
* 每页条数 - 不分页
*
* <p>
* 例如说导出接口可以设置 {@link #pageSize} -1 不分页查询所有数据
*/
public static final Integer PAGE_SIZE_NONE = -1;

View File

@ -8,7 +8,7 @@ import java.io.Serializable;
/**
* 排序字段 DTO
*
* <p>
* 类名加了 ing 的原因是避免和 ES SortField 重名
*/
@Data

View File

@ -16,9 +16,9 @@ public class CacheUtils {
/**
* 构建异步刷新的 LoadingCache 对象
*
* <p>
* 注意如果你的缓存和 ThreadLocal 有关系要么自己处理 ThreadLocal 的传递要么使用 {@link #buildCache(Duration, CacheLoader)} 方法
*
* <p>
* 或者简单理解
* 1相关的使用 {@link #buildCache(Duration, CacheLoader)} 方法
* 2全局系统相关的使用当前缓存方法

View File

@ -28,7 +28,7 @@ public class LocalDateTimeUtils {
/**
* 解析时间
*
* <p>
* 相比 {@link LocalDateTimeUtil#parse(CharSequence)} 方法来说会尽量去解析直到成功
*
* @param time 时间

View File

@ -39,7 +39,7 @@ public class HttpUtils {
/**
* 拼接 URL
*
* <p>
* copy from Spring Security OAuth2 AuthorizationEndpoint 类的 append 方法
*
* @param base 基础 URL

View File

@ -8,7 +8,7 @@ import java.io.IOException;
/**
* Long 序列化规则
*
* <p>
* 会将超长 long 值转换为 string解决前端 JavaScript 最大安全整数是 2^53-1 的问题
*
* @author 星语

View File

@ -4,7 +4,7 @@ import org.apache.skywalking.apm.toolkit.trace.TraceContext;
/**
* 链路追踪工具类
*
* <p>
* 考虑到每个 starter 都需要用到该工具类所以放到 common 模块下的 util 包下
*
* @author 芋道源码

View File

@ -86,7 +86,7 @@ public class MoneyUtils {
/**
* 分转元字符串
*
* <p>
* 例如说 fen 1 则结果为 0.01
*
* @param fen
@ -98,7 +98,7 @@ public class MoneyUtils {
/**
* 金额相乘默认进行四舍五入
*
* <p>
* 位数{@link #PRICE_SCALE}
*
* @param price 金额
@ -114,7 +114,7 @@ public class MoneyUtils {
/**
* 金额相乘百分比默认进行四舍五入
*
* <p>
* 位数{@link #PRICE_SCALE}
*
* @param price 金额

View File

@ -22,7 +22,7 @@ public class NumberUtils {
/**
* 通过经纬度获取地球上两点之间的距离
*
* <p>
* 参考 <<a href="https://gitee.com/dromara/hutool/blob/1caabb586b1f95aec66a21d039c5695df5e0f4c1/hutool-core/src/main/java/cn/hutool/core/util/DistanceUtil.java">DistanceUtil</a>> 实现目前它已经被 hutool 删除
*
* @param lat1 经度1
@ -46,7 +46,7 @@ public class NumberUtils {
/**
* 提供精确的乘法运算
*
* <p>
* hutool {@link NumberUtil#mul(BigDecimal...)} 的差别是如果存在 null则返回 null
*
* @param values 多个被乘值

View File

@ -9,7 +9,7 @@ import java.util.function.Consumer;
/**
* Bean 工具类
*
* <p>
* 1. 默认使用 {@link cn.hutool.core.bean.BeanUtil} 作为实现类虽然不同 bean 工具的性能有差别但是对绝大多数同学的项目不用在意这点性能
* 2. 针对复杂的对象转换可以搜参考 AuthConvert 实现通过 mapstruct + default 配合实现
*

View File

@ -1,7 +1,7 @@
/**
* 对于工具类的选择优先查找 Hutool 中有没对应的方法
* 如果没有则自己封装对应的工具类 Utils 结尾用于区分
*
* <p>
* ps如果担心 Hutool 存在坑的问题可以阅读 Hutool 的实现源码以确保可靠性并且可以补充相关的单元测试
*/
package cn.iocoder.yudao.framework.common.util;

View File

@ -62,7 +62,7 @@ public class DataPermissionContextHolder {
/**
* 清空上下文
*
* <p>
* 目前仅仅用于单测
*/
public static void clear() {

View File

@ -14,7 +14,7 @@ import java.util.List;
/**
* 基于 {@link DataPermissionRule} 的数据权限处理器
*
* <p>
* 它的底层是基于 MyBatis Plus <a href="https://baomidou.com/plugins/data-permission/">数据权限插件</a>
* 核心原理它会在 SQL 执行前拦截 SQL 语句并根据用户权限动态添加权限相关的 SQL 片段这样只有用户有权限访问的数据才会被查询出来
*

View File

@ -17,7 +17,7 @@ public interface DataPermissionRule {
/**
* 返回需要生效的表名数组
* 为什么需要该方法Data Permission 数组基于 SQL 重写通过 Where 返回只有权限的数据
*
* <p>
* 如果需要基于实体名获得表名可调用 {@link TableInfoHelper#getTableInfo(Class)} 获得
*
* @return 表名数组

View File

@ -29,9 +29,9 @@ import java.util.Set;
/**
* 基于部门的 {@link DataPermissionRule} 数据权限规则实现
*
* <p>
* 注意使用 DeptDataPermissionRule 需要保证表中有 dept_id 部门编号的字段可自定义
*
* <p>
* 实际业务场景下会存在一个经典的问题当用户修改部门时冗余的 dept_id 是否需要修改
* 1. 一般情况下dept_id 不进行修改则会导致用户看不到之前的数据yudao-server 采用该方案
* 2. 部分情况下希望该用户还是能看到之前的数据则有两种方式解决需要你改造该 DeptDataPermissionRule 的实现代码
@ -63,7 +63,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
/**
* 基于部门的表字段配置
* 一般情况下每个表的部门编号字段是 dept_id通过该配置自定义
*
* <p>
* key表名
* value字段名
*/
@ -71,7 +71,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
/**
* 基于用户的表字段配置
* 一般情况下每个表的部门编号字段是 dept_id通过该配置自定义
*
* <p>
* key表名
* value字段名
*/

View File

@ -103,16 +103,20 @@ class DataPermissionRuleFactoryImplTest extends BaseMockitoUnitTest {
}
@DataPermission(enable = false)
static class TestClass03 {}
static class TestClass03 {
}
@DataPermission(includeRules = DataPermissionRule01.class)
static class TestClass04 {}
static class TestClass04 {
}
@DataPermission(excludeRules = DataPermissionRule01.class)
static class TestClass05 {}
static class TestClass05 {
}
@DataPermission
static class TestClass06 {}
static class TestClass06 {
}
static class DataPermissionRule01 implements DataPermissionRule {

View File

@ -9,7 +9,7 @@ import java.util.List;
/**
* 区域节点包括国家省份城市地区等信息
*
* <p>
* 数据可见 resources/area.csv 文件
*
* @author 芋道源码
@ -38,7 +38,7 @@ public class Area {
private String name;
/**
* 类型
*
* <p>
* 枚举 {@link AreaTypeEnum}
*/
private Integer type;

View File

@ -138,7 +138,7 @@ public class AreaUtils {
/**
* 格式化区域
*
* <p>
* 例如说
* 1. id = 静安区上海 上海市 静安区
* 2. id = 上海市上海 上海市

View File

@ -10,7 +10,7 @@ import java.io.IOException;
/**
* IP 工具类
*
* <p>
* IP 数据源来自 ip2region.xdb 精简版基于 <a href="https://gitee.com/zhijiantianya/ip2region"/> 项目
*
* @author wanglhup

View File

@ -1,6 +1,6 @@
/**
* IP 拓展支持如下功能
*
* <p>
* 1. IP 功能查询 IP 对应的城市信息
* 基于 https://gitee.com/lionsoul/ip2region 实现
* 2. 城市功能查询城市编码对应的城市信息

View File

@ -27,14 +27,14 @@ public class TenantProperties {
/**
* 需要忽略多租户的请求
*
* <p>
* 默认情况下每个请求需要带上 tenant-id 的请求头但是部分请求是无需带上的例如说短信回调支付回调等 Open API
*/
private Set<String> ignoreUrls = Collections.emptySet();
/**
* 需要忽略多租户的表
*
* <p>
* 即默认所有表都开启多租户的功能所以记得添加对应的 tenant_id 字段哟
*/
private Set<String> ignoreTables = Collections.emptySet();

View File

@ -36,7 +36,8 @@ import org.springframework.data.redis.core.RedisTemplate;
import java.util.Objects;
@AutoConfiguration
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true)
// 允许使用 yudao.tenant.enable=false 禁用多租户
@EnableConfigurationProperties(TenantProperties.class)
public class YudaoTenantAutoConfiguration {

View File

@ -4,7 +4,7 @@ import java.lang.annotation.*;
/**
* 忽略租户标记指定方法不进行租户的自动过滤
*
* <p>
* 注意只有 DB 的场景会过滤其它场景暂时不过滤
* 1Redis 场景因为是基于 Key 实现多租户的能力所以忽略没有意义不像 DB 是一个 column 实现的
* 2MQ 场景有点难以抉择目前可以通过 Consumer 手动在消费的方法上添加 @TenantIgnore 进行忽略

View File

@ -11,7 +11,7 @@ import org.aspectj.lang.annotation.Aspect;
* 忽略多租户的 Aspect基于 {@link TenantIgnore} 注解实现用于一些全局的逻辑
* 例如说一个定时任务读取所有数据进行处理
* 又例如说读取所有数据进行缓存
*
* <p>
* 整体逻辑的实现 {@link TenantUtils#executeIgnore(Runnable)} 需要保持一致
*
* @author 芋道源码

View File

@ -18,7 +18,7 @@ import java.util.concurrent.ConcurrentHashMap;
/**
* 多租户 JobHandler AOP
* 任务执行时会按照租户逐个执行 Job 的逻辑
*
* <p>
* 注意需要保证 JobHandler 的幂等性因为 Job 因为某个租户执行失败重试时之前执行成功的租户也会再次执行
*
* @author 芋道源码

View File

@ -8,7 +8,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
/**
* 多租户的 Kafka {@link EnvironmentPostProcessor} 实现类
*
* <p>
* Kafka Producer 发送消息时增加 {@link TenantKafkaProducerInterceptor} 拦截器
*
* @author 芋道源码

View File

@ -14,7 +14,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
/**
* Kafka 消息队列的多租户 {@link ProducerInterceptor} 实现类
*
* <p>
* 1. Producer 发送消息时 {@link TenantContextHolder} 租户编号添加到消息的 Header
* 2. Consumer 消费消息时将消息的 Header 的租户编号添加到 {@link TenantContextHolder} 通过 {@link InvocableHandlerMethod} 实现
*

View File

@ -11,7 +11,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
/**
* RabbitMQ 消息队列的多租户 {@link ProducerInterceptor} 实现类
*
* <p>
* 1. Producer 发送消息时 {@link TenantContextHolder} 租户编号添加到消息的 Header
* 2. Consumer 消费消息时将消息的 Header 的租户编号添加到 {@link TenantContextHolder} 通过 {@link InvocableHandlerMethod} 实现
*

View File

@ -9,7 +9,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
/**
* 多租户 {@link AbstractRedisMessage} 拦截器
*
* <p>
* 1. Producer 发送消息时 {@link TenantContextHolder} 租户编号添加到消息的 Header
* 2. Consumer 消费消息时将消息的 Header 的租户编号添加到 {@link TenantContextHolder}
*

View File

@ -14,7 +14,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
/**
* RocketMQ 消息队列的多租户 {@link ConsumeMessageHook} 实现类
*
* <p>
* Consumer 消费消息时将消息的 Header 的租户编号添加到 {@link TenantContextHolder} 通过 {@link InvocableHandlerMethod} 实现
*
* @author 芋道源码

View File

@ -8,7 +8,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
/**
* RocketMQ 消息队列的多租户 {@link SendMessageHook} 实现类
*
* <p>
* Producer 发送消息时 {@link TenantContextHolder} 租户编号添加到消息的 Header
*
* @author 芋道源码

View File

@ -10,7 +10,7 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
/**
* 多租户的 {@link RedisCacheManager} 实现类
*
* <p>
* 操作指定 name {@link Cache} 自动拼接租户后缀格式为 name + ":" + tenantId + 后缀
*
* @author airhead

View File

@ -16,7 +16,7 @@ public class TenantUtils {
/**
* 使用指定租户执行对应的逻辑
*
* <p>
* 注意如果当前是忽略租户的情况下会被强制设置成不忽略租户
* 当然执行完成后还是会恢复回去
*
@ -39,7 +39,7 @@ public class TenantUtils {
/**
* 使用指定租户执行对应的逻辑
*
* <p>
* 注意如果当前是忽略租户的情况下会被强制设置成不忽略租户
* 当然执行完成后还是会恢复回去
*

View File

@ -12,6 +12,5 @@
* 2Spring Security
* TransmittableThreadLocalSecurityContextHolderStrategy
* YudaoSecurityAutoConfiguration#securityContextHolderMethodInvokingFactoryBean() 方法
*
*/
package cn.iocoder.yudao.framework.tenant;

View File

@ -38,7 +38,7 @@ import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_
* Extension of {@link HandlerMethod} that invokes the underlying method with
* argument values resolved from the current HTTP request through a list of
* {@link HandlerMethodArgumentResolver}.
*
* <p>
* 针对 rabbitmq-spring kafka-spring不存在合适的拓展点可以实现 Consumer 消费前读取 Header 中的 tenant-id 设置到 {@link TenantContextHolder}
* TODO 芋艿持续跟进看看有没新的拓展点
*
@ -70,6 +70,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
/**
* Construct a new handler method with the given bean instance, method name and parameters.
*
* @param bean the object bean
* @param methodName the method name
* @param parameterTypes the method parameter types
@ -105,6 +106,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
* i.e. without argument resolution.
* <p>Delegates to {@link #getMethodArgumentValues} and calls {@link #doInvoke} with the
* resolved arguments.
*
* @param message the current message being processed
* @param providedArgs "given" arguments matched by type, not resolved
* @return the raw value returned by the invoked method
@ -153,6 +155,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
* Get the method argument values for the current message, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
*
* @since 5.1.2
*/
protected Object[] getMethodArgumentValues(Message<?> message, Object... providedArgs) throws Exception {
@ -175,8 +178,7 @@ public class InvocableHandlerMethod extends HandlerMethod {
}
try {
args[i] = this.resolvers.resolveArgument(parameter, message);
}
catch (Exception ex) {
} catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
@ -197,25 +199,20 @@ public class InvocableHandlerMethod extends HandlerMethod {
protected Object doInvoke(Object... args) throws Exception {
try {
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
} catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
} catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
} else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
} else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
} else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}

View File

@ -1,6 +1,6 @@
/**
* 字典数据模块提供 {@link cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils} 工具类
*
* <p>
* 通过将字典缓存在内存中保证性能
*/
package cn.iocoder.yudao.framework.dict;

View File

@ -4,7 +4,7 @@ import java.lang.annotation.*;
/**
* 字典格式化
*
* <p>
* 实现将字典数据的值格式化成字典数据的标签
*/
@Target({ElementType.FIELD})

View File

@ -4,7 +4,7 @@ import java.lang.annotation.*;
/**
* Excel 列添加下拉选择数据
*
* <p>
* 其中 {@link #dictType()} {@link #functionName()} 二选一
*
* @author HUIHUI

View File

@ -11,7 +11,7 @@ import java.math.RoundingMode;
/**
* 金额转换器
*
* <p>
* 金额单位
*
* @author 芋道源码

View File

@ -4,9 +4,9 @@ import java.util.List;
/**
* Excel 列下拉数据源获取接口
*
* <p>
* 为什么不直接解析字典还搞个接口考虑到有的下拉数据不是从字典中获取的所有需要做一个兼容
*
* @author HUIHUI
*/
public interface ExcelColumnSelectFunction {

View File

@ -37,7 +37,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
/**
* 数据起始行从 0 开始
*
* <p>
* 约定本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
*/
public static final int FIRST_ROW = 1;

View File

@ -9,11 +9,11 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
/**
* {@link org.quartz.Scheduler} 的管理器负责创建任务
*
* <p>
* 考虑到实现的简洁性我们使用 jobHandlerName 作为唯一标识
* 1. Job {@link JobDetail#getKey()}
* 2. Trigger {@link Trigger#getKey()}
*
* <p>
* 另外jobHandlerName 对应到 Spring Bean 的名字直接调用
*
* @author 芋道源码

View File

@ -1,7 +1,7 @@
/**
* 1. 定时任务采用 Quartz 实现进程内的任务执行
* 考虑到高可用使用 Quartz 自带的 MySQL 集群方案
*
* <p>
* 2. 异步任务采用 Spring Async 异步执行
*/
package cn.iocoder.yudao.framework.quartz;

View File

@ -15,7 +15,8 @@ import org.springframework.context.annotation.Bean;
*/
@AutoConfiguration
@ConditionalOnClass({MeterRegistryCustomizer.class})
@ConditionalOnProperty(prefix = "yudao.metrics", value = "enable", matchIfMissing = true) // 允许使用 yudao.metrics.enable=false 禁用 Metrics
@ConditionalOnProperty(prefix = "yudao.metrics", value = "enable", matchIfMissing = true)
// 允许使用 yudao.metrics.enable=false 禁用 Metrics
public class YudaoMetricsAutoConfiguration {
@Bean

View File

@ -4,7 +4,7 @@ import java.lang.annotation.*;
/**
* 打印业务编号 / 业务类型注解
*
* <p>
* 使用时需要设置 SkyWalking OAP Server application.yaml 配置文件修改 SW_SEARCHABLE_TAG_KEYS 配置项
* 增加 biz.type biz.id 两值然后重启 SkyWalking OAP Server 服务器
*

View File

@ -44,7 +44,8 @@ public class YudaoRedisMQConsumerAutoConfiguration {
* 创建 Redis Pub/Sub 广播消费的容器
*/
@Bean
@ConditionalOnBean(AbstractRedisChannelMessageListener.class) // 只有 AbstractChannelMessageListener 存在的时候才需要注册 Redis pubsub 监听
@ConditionalOnBean(AbstractRedisChannelMessageListener.class)
// 只有 AbstractChannelMessageListener 存在的时候才需要注册 Redis pubsub 监听
public RedisMessageListenerContainer redisMessageListenerContainer(
RedisMQTemplate redisMQTemplate, List<AbstractRedisChannelMessageListener<?>> listeners) {
// 创建 RedisMessageListenerContainer 对象
@ -65,7 +66,8 @@ public class YudaoRedisMQConsumerAutoConfiguration {
* 创建 Redis Stream 重新消费的任务
*/
@Bean
@ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候才需要注册 Redis pubsub 监听
@ConditionalOnBean(AbstractRedisStreamMessageListener.class)
// 只有 AbstractStreamMessageListener 存在的时候才需要注册 Redis pubsub 监听
public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners,
RedisMQTemplate redisTemplate,
@Value("${spring.application.name}") String groupName,
@ -75,11 +77,12 @@ public class YudaoRedisMQConsumerAutoConfiguration {
/**
* 创建 Redis Stream 集群消费的容器
*
* <p>
* 基础知识<a href="https://www.geek-book.com/src/docs/redis/redis/redis.io/commands/xreadgroup.html">Redis Stream xreadgroup 命令</a>
*/
@Bean(initMethod = "start", destroyMethod = "stop")
@ConditionalOnBean(AbstractRedisStreamMessageListener.class) // 只有 AbstractStreamMessageListener 存在的时候才需要注册 Redis pubsub 监听
@ConditionalOnBean(AbstractRedisStreamMessageListener.class)
// 只有 AbstractStreamMessageListener 存在的时候才需要注册 Redis pubsub 监听
public StreamMessageListenerContainer<String, ObjectRecord<String, String>> redisStreamMessageListenerContainer(
RedisMQTemplate redisMQTemplate, List<AbstractRedisStreamMessageListener<?>> listeners) {
RedisTemplate<String, ?> redisTemplate = redisMQTemplate.getRedisTemplate();

View File

@ -27,7 +27,7 @@ public class RedisPendingMessageResendJob {
/**
* 消息超时时间默认 5 分钟
*
* <p>
* 1. 超时的消息才会被重新投递
* 2. 由于定时任务 1 分钟一次消息超时后不会被立即重投极端情况下消息5分钟过期后再等 1 分钟才会被扫瞄到
*/

View File

@ -17,7 +17,6 @@ import java.util.List;
* Redis Pub/Sub 监听器抽象类用于实现广播消费
*
* @param <T> 消息类型一定要填写噢不然会报错
*
* @author 芋道源码
*/
public abstract class AbstractRedisChannelMessageListener<T extends AbstractRedisChannelMessage> implements MessageListener {

View File

@ -19,7 +19,6 @@ import java.util.List;
* Redis Stream 监听器抽象类用于实现集群消费
*
* @param <T> 消息类型一定要填写噢不然会报错
*
* @author 芋道源码
*/
public abstract class AbstractRedisStreamMessageListener<T extends AbstractRedisStreamMessage>

View File

@ -2,10 +2,10 @@ package cn.iocoder.yudao.framework.datasource.core.enums;
/**
* 对应于多数据源中不同数据源配置
*
* <p>
* 通过在方法上使用 {@link com.baomidou.dynamic.datasource.annotation.DS} 注解设置使用的数据源
* 注意默认是 {@link #MASTER} 数据源
*
* <p>
* 对应官方文档为 http://dynamic-datasource.com/guide/customize/Annotation.html
*/
public interface DataSourceEnum {

View File

@ -21,7 +21,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
*
* @author 芋道源码
*/
@AutoConfiguration(before = MybatisPlusAutoConfiguration.class) // 目的先于 MyBatis Plus 自动配置避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志
@AutoConfiguration(before = MybatisPlusAutoConfiguration.class)
// 目的先于 MyBatis Plus 自动配置避免 @MapperScan 可能扫描不到 Mapper 打印 warn 日志
@MapperScan(value = "${yudao.info.base-package}", annotationClass = Mapper.class,
lazyInitialization = "${mybatis.lazy-initialization:false}") // Mapper 懒加载目前仅用于单元测试
public class YudaoMybatisAutoConfiguration {

View File

@ -13,7 +13,7 @@ import java.time.LocalDateTime;
/**
* 基础实体对象
*
* <p>
* 为什么实现 {@link TransPojo} 接口
* 因为使用 Easy-Trans TransType.SIMPLE 模式集成 MyBatis Plus 查询
*
@ -35,14 +35,14 @@ public abstract class BaseDO implements Serializable, TransPojo {
private LocalDateTime updateTime;
/**
* 创建者目前使用 SysUser id 编号
*
* <p>
* 使用 String 类型的原因是未来可能会存在非数值的情况留好拓展性
*/
@TableField(fill = FieldFill.INSERT, jdbcType = JdbcType.VARCHAR)
private String creator;
/**
* 更新者目前使用 SysUser id 编号
*
* <p>
* 使用 String 类型的原因是未来可能会存在非数值的情况留好拓展性
*/
@TableField(fill = FieldFill.INSERT_UPDATE, jdbcType = JdbcType.VARCHAR)

View File

@ -30,7 +30,7 @@ public enum DbTypeEnum {
/**
* PostgreSQL
*
* <p>
* 华为 openGauss 使用 ProductName PostgreSQL 相同
*/
POSTGRE_SQL(DbType.POSTGRE_SQL, "PostgreSQL", "POSITION('#{value}' IN #{column}) <> 0"),

View File

@ -10,7 +10,7 @@ import java.util.Objects;
/**
* 通用参数填充实现类
*
* <p>
* 如果没有显式的对通用参数进行赋值这里会对通用参数进行填充赋值
*
* @author hexiaowu

View File

@ -26,7 +26,7 @@ import java.util.Objects;
/**
* MyBatis Plus BaseMapper 的基础上拓展提供更多的能力
*
* <p>
* 1. {@link BaseMapper} MyBatis Plus 的基础接口提供基础的 CRUD 能力
* 2. {@link MPJBaseMapper} MyBatis Plus Join 的基础接口提供连表 Join 能力
*/

View File

@ -11,7 +11,7 @@ import java.util.Collection;
/**
* 拓展 MyBatis Plus QueryWrapper 主要增加如下功能
*
* <p>
* 1. 拼接条件的方法增加 xxxIfPresent 方法用于判断值不存在的时候不要拼接到条件中
*
* @param <T> 数据类型
@ -141,7 +141,7 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
/**
* 设置只返回最后一条
*
* <p>
* TODO 芋艿不是完美解需要在思考下如果使用多数据源并且数据源是多种类型时可能会存在问题实现之返回一条的语法不同
*
* @return this

View File

@ -21,7 +21,7 @@ public class TranslateUtils {
/**
* 数据翻译
*
* <p>
* 使用场景无法使用 @TransMethodResult 注解的场景只能通过手动触发翻译
*
* @param data 数据

View File

@ -22,10 +22,11 @@ public @interface Idempotent {
/**
* 幂等的超时时间默认为 1
*
* <p>
* 注意如果执行时间超过它请求还是会进来
*/
int timeout() default 1;
/**
* 时间单位默认为 SECONDS
*/
@ -44,6 +45,7 @@ public @interface Idempotent {
* @see ExpressionIdempotentKeyResolver 自定义表达式通过 {@link #keyArg()} 计算
*/
Class<? extends IdempotentKeyResolver> keyResolver() default DefaultIdempotentKeyResolver.class;
/**
* 使用的 Key 参数
*/
@ -51,10 +53,10 @@ public @interface Idempotent {
/**
* 删除 Key当发生异常时候
*
* <p>
* 问题为什么发生异常时需要删除 Key
* 回答发生异常时说明业务发生错误此时需要删除 Key避免下次请求无法正常执行
*
* <p>
* 问题为什么不搞 deleteWhenSuccess 执行成功时需要删除 Key
* 回答这种情况下本质上是分布式锁推荐使用 @Lock4j 注解
*/

View File

@ -8,7 +8,7 @@ import org.aspectj.lang.JoinPoint;
/**
* 默认全局级别幂等 Key 解析器使用方法名 + 方法参数组装成一个 Key
*
* <p>
* 为了避免 Key 过长使用 MD5 进行压缩
*
* @author 芋道源码

View File

@ -9,7 +9,7 @@ import org.aspectj.lang.JoinPoint;
/**
* 用户级别的幂等 Key 解析器使用方法名 + 方法参数 + userId + userType组装成一个 Key
*
* <p>
* 为了避免 Key 过长使用 MD5 进行压缩
*
* @author 芋道源码

Some files were not shown because too many files have changed in this diff Show More