Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add upload gift card page #510

Merged
merged 2 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type { ChatMessage } from './chatgpt'
import { abortChatProcess, chatConfig, chatReplyProcess, containsSensitiveWords, initAuditService } from './chatgpt'
import { auth, getUserId } from './middleware/auth'
import { clearApiKeyCache, clearConfigCache, getApiKeys, getCacheApiKeys, getCacheConfig, getOriginConfig } from './storage/config'
import type { AnnounceConfig, AuditConfig, ChatInfo, ChatOptions, Config, KeyConfig, MailConfig, SiteConfig, UserConfig, UserInfo } from './storage/model'
import type { AnnounceConfig, AuditConfig, ChatInfo, ChatOptions, Config, GiftCard, KeyConfig, MailConfig, SiteConfig, UserConfig, UserInfo } from './storage/model'
import { AdvancedConfig, Status, UsageResponse, UserRole } from './storage/model'
import {
clearChat,
Expand Down Expand Up @@ -39,6 +39,7 @@ import {
updateChat,
updateConfig,
updateGiftCard,
updateGiftCards,
updateRoomChatModel,
updateRoomPrompt,
updateRoomUsingContext,
Expand Down Expand Up @@ -888,6 +889,18 @@ router.post('/redeem-card', auth, async (req, res) => {
}
})

// update giftcard database
router.post('/giftcard-update', rootAuth, async (req, res) => {
try {
const { data, overRideSwitch } = req.body as { data: GiftCard[];overRideSwitch: boolean }
await updateGiftCards(data, overRideSwitch)
res.send({ status: 'Success', message: '更新成功 | Update successfully' })
}
catch (error) {
res.send({ status: 'Fail', message: error.message, data: null })
}
})

router.post('/user-chat-model', auth, async (req, res) => {
try {
const { chatModel } = req.body as { chatModel: string }
Expand Down
10 changes: 10 additions & 0 deletions service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ export async function updateAmountMinusOne(userId: string) {
return result.modifiedCount > 0
}

// update giftcards database
export async function updateGiftCards(data: GiftCard[], overRide = true) {
if (overRide) {
// i am not sure is there a drop option for the node driver reference https://mongodb.github.io/node-mongodb-native/6.4/
await redeemCol.deleteMany({})
BobDu marked this conversation as resolved.
Show resolved Hide resolved
}
const insertResult = await redeemCol.insertMany(data)
return insertResult
}

export async function insertChat(uuid: number, text: string, images: string[], roomId: number, options?: ChatOptions) {
const chatInfo = new ChatInfo(roomId, uuid, text, images, options)
await chatCol.insertOne(chatInfo)
Expand Down
9 changes: 8 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { AxiosProgressEvent, GenericAbortSignal } from 'axios'
import { get, post } from '@/utils/request'
import type { AnnounceConfig, AuditConfig, ConfigState, KeyConfig, MailConfig, SiteConfig, Status, UserInfo, UserPassword } from '@/components/common/Setting/model'
import type { AnnounceConfig, AuditConfig, ConfigState, GiftCard, KeyConfig, MailConfig, SiteConfig, Status, UserInfo, UserPassword } from '@/components/common/Setting/model'
import { useAuthStore, useUserStore } from '@/store'
import type { SettingsState } from '@/store/modules/user/helper'

Expand Down Expand Up @@ -153,6 +153,13 @@ export function decode_redeemcard<T = any>(redeemCardNo: string) {
})
}

export function fetchUpdateGiftCards<T = any>(data: GiftCard[], overRideSwitch: boolean) {
return post<T>({
url: '/giftcard-update',
data: { data, overRideSwitch },
})
}

export function fetchUpdateUserChatModel<T = any>(chatModel: string) {
return post<T>({
url: '/user-chat-model',
Expand Down
163 changes: 163 additions & 0 deletions src/components/common/Setting/Gift.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
<script setup lang='ts'>
import { ref } from 'vue'
import type { UploadFileInfo } from 'naive-ui'
import { NButton, NDataTable, NDivider, NIcon, NP, NSpace, NSwitch, NText, NUpload, NUploadDragger, useMessage } from 'naive-ui'
import type { GiftCard } from './model'
import { SvgIcon } from '@/components/common'
import { fetchUpdateGiftCards } from '@/api'
import { t } from '@/locales'

const ms = useMessage()
const loading = ref(false)
const overRideSwitch = ref(true)
const fileListRef = ref<UploadFileInfo[]>([])

const handleSaving = ref(false)
const columns = [
{
title: 'cardno',
key: 'cardno',
resizable: true,
width: 100,
minWidth: 100,
maxWidth: 200,
},
{
title: 'amount',
key: 'amount',
width: 80,
},
{
title: 'redeemed',
key: 'redeemed',
width: 100,
},
]

const csvData = ref<Array<GiftCard>>([])

// const csvData: giftcard[] = [
// {
// cardno: 'dfsdfasf',
// amount: 10,
// redeemed: 0,
// },
// {
// cardno: 'ooioioo',
// amount: 20,
// redeemed: 0,
// },
// {
// cardno: '765653',
// amount: 30,
// redeemed: 1,
// },
// ]

function readfile(file: Blob) {
try {
// const file = event.target.files[0]
if (file) {
ms.info('生成预览中 | Generating Preview')
const reader = new FileReader()
reader.onload = (e) => {
const contents = e.target?.result as string
csvData.value = parseCSV(contents)
}
reader.readAsText(file)
}
else {
ms.info('没有读取到文件 | No file find')
}
}
catch (error: any) {
ms.info(`读取文件出错 | Error reading file | ${error.message}`)
}
}

function parseCSV(content: string) {
const rows = content.trim().split(/\r?\n/)
// const headers = rows[0].split(',')
const giftCards: GiftCard[] = rows.slice(1).map(row => row.split(',')).map(row => ({
cardno: row[0],
amount: Number(row[1].trim()),
redeemed: Number(row[2].trim()),
}))
return giftCards
}

function handleUploadChange(data: { file: UploadFileInfo, fileList: Array<UploadFileInfo>, event?: Event }) {
fileListRef.value = data.fileList
csvData.value = []
if (data.event) {
const file_bolb = data.fileList[0].file
if (file_bolb)
readfile(file_bolb)
}
}

async function uploadGiftCards() {
handleSaving.value = true
try {
if (csvData.value.length > 0)
await fetchUpdateGiftCards(csvData.value, overRideSwitch.value)
ms.success(`${t('common.success')}`)
}
catch (error: any) {
ms.error(`Failed update DB ${error.message}`)
}

handleSaving.value = false
}
</script>

<template>
<div class="p-4 space-y-5 min-h-[300px]">
<div class="space-y-6">
<NUpload
:max="1"
accept=".csv"
:on-change="handleUploadChange"
>
<NUploadDragger>
<div style="margin-bottom: 12px">
<NIcon size="48" :depth="3">
<SvgIcon icon="mage:box-upload" />
</NIcon>
</div>
<NText style="font-size: 16px">
点击或者拖动文件到该区域来上传|Upload CSV
</NText>
<NP depth="3" style="margin: 8px 0 0 0">
请不要上传敏感数据,文件仅限csv(2k行内),表头为cardno,amount,redeemed<br>
warning: duplicated cardno will not be detected in this process
</NP>
</NUploadDragger>
</NUpload>

<NSpace vertical :size="12">
<span class="flex-shrink-0 w-[100px]">Data Preview(Top 30) & Due to body-parser limits csv files >2k rows not supported </span>
<NDataTable
remote
:loading="loading"
:row-key="(rowData:GiftCard) => rowData.cardno"
:columns="columns"
:data="csvData.slice(0, 30)"
:max-height="200"
/>
</NSpace>
</div>
<NDivider />
<div class="flex items-center space-x-4">
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.overRide') }}</span>
<div class="flex-1">
<NSwitch v-model:value="overRideSwitch" />
</div>
<div class="flex-1">
<NButton type="primary" :loading="handleSaving" size="large" @click="uploadGiftCards()">
{{ $t('setting.uploadgifts') }}
</NButton>
</div>
</div>
</div>
</template>
8 changes: 8 additions & 0 deletions src/components/common/Setting/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import About from './About.vue'
import Site from './Site.vue'
import Mail from './Mail.vue'
import Audit from './Audit.vue'
import Gift from './Gift.vue'
import User from './User.vue'
import Key from './Keys.vue'
import Password from './Password.vue'
Expand Down Expand Up @@ -141,6 +142,13 @@ const show = computed({
</template>
<Key />
</NTabPane>
<NTabPane v-if="userStore.userInfo.root" name="GiftCardConfig" tab="GiftCardConfig">
<template #tab>
<SvgIcon class="text-lg" icon="mdi-gift" />
<span class="ml-2">{{ $t('setting.uploadgifts') }}</span>
</template>
<Gift />
</NTabPane>
</NTabs>
</div>
</NModal>
Expand Down
6 changes: 6 additions & 0 deletions src/components/common/Setting/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,9 @@ export class TwoFAConfig {
this.testCode = ''
}
}

export interface GiftCard {
cardno: string
amount: number
redeemed: number
}
2 changes: 2 additions & 0 deletions src/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export default {
disable2FAConfirm: 'Are you sure to disable 2FA for this user?',
},
setting: {
overRide: 'Enable Override',
uploadgifts: 'Upload Redemption Code',
announceConfig: 'Announcement',
announceEnabled: 'Open Announcement',
announceWords: 'Announcement Content',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export default {
disable2FAConfirm: 'Are you sure to disable 2FA for this user?',
},
setting: {
overRide: '덮어쓰기 활성화',
uploadgifts: '교환 코드 업로드',
announceConfig: '网站公告',
announceEnabled: 'Open Announcement',
announceWords: 'Announcement Content',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export default {
disable2FAConfirm: '您确定要为此用户禁用两步验证吗??',
},
setting: {
overRide: '开启覆写',
uploadgifts: '上传兑换码',
announceConfig: '网站公告',
announceEnabled: '公告开关',
announceWords: '公告内容',
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ export default {
disable2FAConfirm: '您确定要为此用户禁用两步验证吗??',
},
setting: {
overRide: '開啟覆寫',
uploadgifts: '上傳兌換碼',
announceConfig: '网站公告',
announceEnabled: '打开公告',
announceWords: '公告内容',
Expand Down
Loading