add user lock for chat api, Prevent insufficient deduction of user power caused by submitting multiple requests at one time

This commit is contained in:
GeekMaster
2025-09-12 15:05:14 +08:00
parent 65fb58585c
commit c5badb3e13
43 changed files with 309 additions and 429 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -178,10 +178,13 @@ const props = defineProps({
const selectedModel = ref(props.items[0])
const requiredKeys = ref(props.requiredKeys)
const emit = defineEmits(['update:modelValue'])
const emit = defineEmits(['update:modelValue', 'update:requiredKeys'])
// 初始化 modelValue 默认值
const initModelValue = (model) => {
if (props.items.length === 0) {
return {}
}
const defaultValues = {}
requiredKeys.value = {}
if (model && model.params) {
@@ -250,6 +253,7 @@ watch(
() => props.items,
(newValue) => {
selectedModel.value = newValue[0]
modelValue.value = initModelValue(selectedModel.value)
},
{ deep: true }
)

View File

@@ -102,76 +102,7 @@ export const JimengParams = {
},
],
},
{
name: '图片 2.1 文生图',
version: '2.1',
label: '平面绘感强,可生成文字海报',
key: 'jimeng_high_aes_general_v21_L',
params: [
{
name: 'prompt',
label: '提示词',
type: 'textarea',
showWordLimit: true,
maxlength: 800,
autosize: { minRows: 3, maxRows: 5 },
required: true,
placeholder: '请输入提示词',
info: '用于生成图像的提示词 ,中英文均可输入',
},
{
name: 'size',
type: 'select',
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '21:9 (1195 * 512)',
value: '1195x512',
},
{
label: '16:9 (1024 * 576)',
value: '1024x576',
},
{
label: '3:2 (1024 * 682)',
value: '1024x682',
},
{
label: '4:3 (1024 * 768)',
value: '1024x768',
},
{
label: '1:1 (1024 * 1024)',
value: '1024x1024',
},
{
label: '3:4 (768 * 1024)',
value: '768x1024',
},
{
label: '2:3 (682 * 1024)',
value: '682x1024',
},
{
label: '9:16 (576 * 1024)',
value: '576x1024',
},
],
},
{
name: 'use_pre_llm',
type: 'switch',
required: false,
label: '开启文本扩写',
info: '开启后,系统会自动扩写提示词,提高生成质量',
value: true,
},
],
},
{
name: '图片 3.0 文生图',
version: '3.0',
@@ -354,6 +285,17 @@ export const JimengParams = {
accept: '.png,.jpg,.jpeg',
info: '长边与短边比例在3以内超出此比例或比例相对极端会导致报错。',
},
{
name: 'scale',
label: '文本描述影响的程度',
type: 'slider',
required: true,
info: '该值越大代表文本描述影响程度越大,且输入图片影响程度越小',
min: 0,
max: 1,
step: 0.1,
value: 0.5,
},
{
name: 'size',
type: 'select',
@@ -405,17 +347,6 @@ export const JimengParams = {
label: '将输入的单人写真图片,进行有创意的特效化处理。',
key: 'i2i_multi_style_zx2x',
params: [
{
name: 'prompt',
label: '提示词',
type: 'textarea',
required: true,
showWordLimit: true,
maxlength: 800,
autosize: { minRows: 3, maxRows: 5 },
placeholder: '请输入用于编辑图像的提示词把xxx改成xxx删除xxx添加xxx等',
info: '建议长度<=120字符最长不超过800字符',
},
{
name: 'image_urls',
label: '参考图片',
@@ -610,6 +541,77 @@ export const JimengParams = {
},
],
},
{
name: '图片 2.1 文生图',
version: '2.1',
label: '平面绘感强,可生成文字海报',
key: 'jimeng_high_aes_general_v21_L',
params: [
{
name: 'prompt',
label: '提示词',
type: 'textarea',
showWordLimit: true,
maxlength: 800,
autosize: { minRows: 3, maxRows: 5 },
required: true,
placeholder: '请输入提示词',
info: '用于生成图像的提示词 ,中英文均可输入',
},
{
name: 'size',
type: 'select',
required: true,
placeholder: '请选择尺寸',
label: '图片尺寸',
prefix: 'icon-resize',
options: [
{
label: '21:9 (1195 * 512)',
value: '1195x512',
},
{
label: '16:9 (1024 * 576)',
value: '1024x576',
},
{
label: '3:2 (1024 * 682)',
value: '1024x682',
},
{
label: '4:3 (1024 * 768)',
value: '1024x768',
},
{
label: '1:1 (1024 * 1024)',
value: '1024x1024',
},
{
label: '3:4 (768 * 1024)',
value: '768x1024',
},
{
label: '2:3 (682 * 1024)',
value: '682x1024',
},
{
label: '9:16 (576 * 1024)',
value: '576x1024',
},
],
},
{
name: 'use_pre_llm',
type: 'switch',
required: false,
label: '开启文本扩写',
info: '开启后,系统会自动扩写提示词,提高生成质量',
value: true,
},
],
},
],
video: [
// 视频 3.0 720P-文生视频

View File

@@ -28,8 +28,6 @@ export const useJimengStore = defineStore('jimeng', () => {
// 用户信息
const isLogin = ref(false)
const userPower = ref(100)
// 视频预览
const showDialog = ref(false)
const currentVideoUrl = ref('')
@@ -37,16 +35,9 @@ export const useJimengStore = defineStore('jimeng', () => {
// 登录弹窗
const shareStore = useSharedStore()
// 新增:动态获取算力消耗配置
// 积分消耗配置
const powerConfig = reactive({})
// 动态设置算力消耗
const setFunctionPowers = (config) => {
functions.forEach((f) => {
if (config[f.key] !== undefined) {
f.power = config[f.key]
}
})
}
const currentPowerCost = ref('0积分')
// 功能配置
const functions = JimengFunctions
@@ -69,11 +60,7 @@ export const useJimengStore = defineStore('jimeng', () => {
const switchFunction = (f) => {
activeFunction.value = f.key
formData.value = {}
}
// 获取当前算力消耗
const getCurrentPowerCost = () => {
return activeFunction.value.power
setFunctionPowers()
}
// 获取功能名称
@@ -198,12 +185,9 @@ export const useJimengStore = defineStore('jimeng', () => {
shareStore.setShowLoginDialog(true)
return
}
// if (userPower.value < currentPowerCost.value) {
// showMessageError('算力不足')
// return
// }
console.log(formData.value)
for (const key in requiredKeys.value) {
if (!formData.value[key].required) {
if (!formData.value[key]) {
showMessageError('缺少参数:' + requiredKeys.value[key].label)
return
}
@@ -296,18 +280,25 @@ export const useJimengStore = defineStore('jimeng', () => {
showDialog.value = true
}
const setFunctionPowers = () => {
if (activeFunction.value === 'image') {
currentPowerCost.value = `${powerConfig.image}积分/张`
} else {
currentPowerCost.value = `${powerConfig.video}积分/秒`
}
}
// 初始化方法
const init = async () => {
try {
// 获取算力消耗配置
// 获取积分消耗配置
const powerRes = await httpGet('/api/jimeng/power-config')
if (powerRes.data) {
Object.assign(powerConfig, powerRes.data)
setFunctionPowers(powerRes.data)
setFunctionPowers()
}
const user = await checkSession()
isLogin.value = true
userPower.value = user.power
// 获取任务列表
await fetchData(1)
// 开始轮询
@@ -335,7 +326,6 @@ export const useJimengStore = defineStore('jimeng', () => {
currentList,
isOver,
isLogin,
userPower,
showDialog,
currentVideoUrl,
@@ -346,11 +336,11 @@ export const useJimengStore = defineStore('jimeng', () => {
formData,
requiredKeys,
progress,
currentPowerCost,
// 方法
init,
switchFunction,
getCurrentPowerCost,
getFunctionName,
getTaskStatusText,
getTaskType,

View File

@@ -140,7 +140,7 @@
<!-- 功能开关 -->
<div class="function-params">
<div class="mb-3">
<div class="mb-2">
<div class="mb-2" v-if="store.functionParams[store.activeFunction].length > 0">
<label class="label text-left font-bold">模型选择</label>
</div>
<param-builder
@@ -152,7 +152,10 @@
</div>
<!-- 提交按钮 -->
<div class="submit-btn flex justify-center pt-4">
<div
class="submit-btn flex justify-center pt-4"
v-if="store.functionParams[store.activeFunction].length > 0"
>
<button
@click="store.submitTask"
:disabled="store.submitting"
@@ -161,7 +164,7 @@
>
<i v-if="store.submitting" class="iconfont icon-loading animate-spin"></i>
<i v-else class="iconfont icon-chuangzuo"></i>
<span>立即生成 ({{ store.currentPowerCost }}算力)</span>
<span>立即生成 ({{ store.currentPowerCost }})</span>
</button>
</div>
</div>
@@ -342,7 +345,7 @@
</div>
<div class="task-meta">
<span>{{ dateFormat(item.created_at) }}</span>
<span v-if="item.power">{{ item.power }}算力</span>
<span v-if="item.power">{{ item.power }}积分</span>
</div>
</div>
</div>

View File

@@ -57,9 +57,8 @@
<el-form-item>
<template #label>
<div class="text-gray-500 text-sm">
生成图片消耗的积分包括文生图图生图图片编辑图片特效<span
class="text-red-500"
>单位积分/</span
生成图片消耗的积分包括文生图图生图图片编辑图片特效<el-tag type="primary"
>单位积分/</el-tag
>
</div>
</template>
@@ -72,8 +71,8 @@
<el-form-item>
<template #label>
<div class="text-gray-500 text-sm">
生成视频消耗的积分包括文生视频图生视频<span class="text-red-500"
>单位积分/</span
生成视频消耗的积分包括文生视频图生视频<el-tag type="primary"
>单位积分/</el-tag
>
</div>
</template>
@@ -86,7 +85,7 @@
<el-form-item>
<template #label>
<div class="text-gray-500 text-sm">
生成数字人视频消耗的积分<span class="text-red-500">单位积分/</span>
生成数字人视频消耗的积分<el-tag type="primary">单位积分/</el-tag>
</div>
</template>
<el-input-number
@@ -98,7 +97,7 @@
<el-form-item>
<template #label>
<div class="text-gray-500 text-sm">
生成视频动作迁移消耗的积分<span class="text-red-500">单位积分/</span>
生成视频动作迁移消耗的积分<el-tag type="primary">单位积分/</el-tag>
</div>
</template>
<el-input-number