feat: 日志持久化存储到文件+管理员后台查看/清空日志页面
This commit is contained in:
@@ -14,6 +14,7 @@ func main() {
|
|||||||
config.Load()
|
config.Load()
|
||||||
|
|
||||||
utils.InitDB()
|
utils.InitDB()
|
||||||
|
utils.InitLogger()
|
||||||
utils.AutoMigrate()
|
utils.AutoMigrate()
|
||||||
|
|
||||||
if config.AppConfig.Server.Mode == "release" {
|
if config.AppConfig.Server.Mode == "release" {
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"sale/internal/utils"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogHandler struct{}
|
||||||
|
|
||||||
|
func NewLogHandler() *LogHandler {
|
||||||
|
return &LogHandler{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LogHandler) GetLogs(c *gin.Context) {
|
||||||
|
lines := 500
|
||||||
|
if l := c.Query("lines"); l != "" {
|
||||||
|
if v, err := strconv.Atoi(l); err == nil && v > 0 {
|
||||||
|
lines = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logs, err := utils.GetLogs(lines)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read logs"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"data": logs})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LogHandler) ClearLogs(c *gin.Context) {
|
||||||
|
if err := utils.ClearLogs(); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to clear logs"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Logs cleared successfully"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *LogHandler) GetLogInfo(c *gin.Context) {
|
||||||
|
size, err := utils.GetLogSize()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get log info"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
modTime, err := utils.GetLogModTime()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get log info"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizeStr string
|
||||||
|
if size >= 1024*1024 {
|
||||||
|
sizeStr = fmt.Sprintf("%.2f MB", float64(size)/(1024*1024))
|
||||||
|
} else if size >= 1024 {
|
||||||
|
sizeStr = fmt.Sprintf("%.2f KB", float64(size)/1024)
|
||||||
|
} else {
|
||||||
|
sizeStr = fmt.Sprintf("%d B", size)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"data": gin.H{
|
||||||
|
"size": size,
|
||||||
|
"size_str": sizeStr,
|
||||||
|
"mod_time": modTime,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -25,6 +25,7 @@ func SetupRoutes(r *gin.Engine) {
|
|||||||
bannerHandler := handlers.NewBannerHandler()
|
bannerHandler := handlers.NewBannerHandler()
|
||||||
paymentChannelHandler := handlers.NewPaymentChannelHandler()
|
paymentChannelHandler := handlers.NewPaymentChannelHandler()
|
||||||
paymentHandler := handlers.NewPaymentHandler()
|
paymentHandler := handlers.NewPaymentHandler()
|
||||||
|
logHandler := handlers.NewLogHandler()
|
||||||
|
|
||||||
r.Use(middlewares.CORSMiddleware())
|
r.Use(middlewares.CORSMiddleware())
|
||||||
|
|
||||||
@@ -235,6 +236,10 @@ func SetupRoutes(r *gin.Engine) {
|
|||||||
adminPaymentChannels.DELETE("/:id", paymentChannelHandler.Delete)
|
adminPaymentChannels.DELETE("/:id", paymentChannelHandler.Delete)
|
||||||
adminPaymentChannels.PUT("/:id/toggle", paymentChannelHandler.Toggle)
|
adminPaymentChannels.PUT("/:id/toggle", paymentChannelHandler.Toggle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
admin.GET("/logs", logHandler.GetLogs)
|
||||||
|
admin.DELETE("/logs", logHandler.ClearLogs)
|
||||||
|
admin.GET("/logs/info", logHandler.GetLogInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
var logFilePath = "data/app.log"
|
||||||
|
|
||||||
|
func InitLogger() {
|
||||||
|
logDir := filepath.Dir(logFilePath)
|
||||||
|
if logDir != "." && logDir != "/" {
|
||||||
|
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||||||
|
log.Fatalf("Failed to create log directory %s: %v", logDir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to open log file %s: %v", logFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
multiWriter := io.MultiWriter(os.Stdout, file)
|
||||||
|
log.SetOutput(multiWriter)
|
||||||
|
|
||||||
|
log.Printf("Logger initialized, log file: %s", logFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogs(lines int) ([]string, error) {
|
||||||
|
file, err := os.Open(logFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
ring := make([]string, 0, lines)
|
||||||
|
idx := 0
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
buf := make([]byte, 0, 64*1024)
|
||||||
|
scanner.Buffer(buf, 1024*1024)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if len(ring) < cap(ring) {
|
||||||
|
ring = append(ring, line)
|
||||||
|
} else {
|
||||||
|
ring[idx] = line
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % cap(ring)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ring) < cap(ring) {
|
||||||
|
return ring, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]string, len(ring))
|
||||||
|
for i := 0; i < len(ring); i++ {
|
||||||
|
result[i] = ring[(idx+i)%len(ring)]
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ClearLogs() error {
|
||||||
|
file, err := os.OpenFile(logFilePath, os.O_WRONLY|os.O_TRUNC, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogSize() (int64, error) {
|
||||||
|
info, err := os.Stat(logFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return info.Size(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLogModTime() (interface{}, error) {
|
||||||
|
info, err := os.Stat(logFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return info.ModTime(), nil
|
||||||
|
}
|
||||||
@@ -180,4 +180,8 @@ export const adminApi = {
|
|||||||
updatePaymentChannel: (id: number, data: any) => api.put(`/admin/payment-channels/${id}`, data),
|
updatePaymentChannel: (id: number, data: any) => api.put(`/admin/payment-channels/${id}`, data),
|
||||||
deletePaymentChannel: (id: number) => api.delete(`/admin/payment-channels/${id}`),
|
deletePaymentChannel: (id: number) => api.delete(`/admin/payment-channels/${id}`),
|
||||||
togglePaymentChannel: (id: number) => api.put(`/admin/payment-channels/${id}/toggle`),
|
togglePaymentChannel: (id: number) => api.put(`/admin/payment-channels/${id}/toggle`),
|
||||||
|
|
||||||
|
getLogs: (lines?: number) => api.get('/admin/logs', { params: { lines: lines || 500 } }),
|
||||||
|
clearLogs: () => api.delete('/admin/logs'),
|
||||||
|
getLogInfo: () => api.get('/admin/logs/info'),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,10 @@
|
|||||||
<el-icon><Wallet /></el-icon>
|
<el-icon><Wallet /></el-icon>
|
||||||
<span class="nav-text">{{ $t('admin.paymentManagement') }}</span>
|
<span class="nav-text">{{ $t('admin.paymentManagement') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<router-link to="/admin/logs" class="nav-link">
|
||||||
|
<el-icon><Notebook /></el-icon>
|
||||||
|
<span class="nav-text">日志管理</span>
|
||||||
|
</router-link>
|
||||||
<router-link to="/admin/settings" class="nav-link">
|
<router-link to="/admin/settings" class="nav-link">
|
||||||
<el-icon><Setting /></el-icon>
|
<el-icon><Setting /></el-icon>
|
||||||
<span class="nav-text">{{ $t('admin.systemSettings') }}</span>
|
<span class="nav-text">{{ $t('admin.systemSettings') }}</span>
|
||||||
@@ -160,7 +164,7 @@
|
|||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { DataAnalysis, User, Folder, Stamp, Goods, List, Van, Trophy, ChatDotSquare, Setting, Document, Picture, Compass, Fold, Expand, HomeFilled, FullScreen, ArrowDown, SwitchButton, ArrowRight, Wallet } from '@element-plus/icons-vue'
|
import { DataAnalysis, User, Folder, Stamp, Goods, List, Van, Trophy, ChatDotSquare, Setting, Document, Picture, Compass, Fold, Expand, HomeFilled, FullScreen, ArrowDown, SwitchButton, ArrowRight, Wallet, Notebook } from '@element-plus/icons-vue'
|
||||||
import { useUserStore } from '../store/user'
|
import { useUserStore } from '../store/user'
|
||||||
import { useCartStore } from '../store/cart'
|
import { useCartStore } from '../store/cart'
|
||||||
|
|
||||||
@@ -187,6 +191,7 @@ const pageTitles: Record<string, string> = {
|
|||||||
'/admin/articles': '资讯管理',
|
'/admin/articles': '资讯管理',
|
||||||
'/admin/banners': '轮播图管理',
|
'/admin/banners': '轮播图管理',
|
||||||
'/admin/payment': '支付管理',
|
'/admin/payment': '支付管理',
|
||||||
|
'/admin/logs': '日志管理',
|
||||||
'/admin/settings': '系统设置'
|
'/admin/settings': '系统设置'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ const routes = [
|
|||||||
{ path: 'articles', name: 'AdminArticles', component: () => import('../views/admin/Articles.vue') },
|
{ path: 'articles', name: 'AdminArticles', component: () => import('../views/admin/Articles.vue') },
|
||||||
{ path: 'banners', name: 'AdminBanners', component: () => import('../views/admin/Banners.vue') },
|
{ path: 'banners', name: 'AdminBanners', component: () => import('../views/admin/Banners.vue') },
|
||||||
{ path: 'payment', name: 'AdminPayment', component: () => import('../views/admin/PaymentChannels.vue') },
|
{ path: 'payment', name: 'AdminPayment', component: () => import('../views/admin/PaymentChannels.vue') },
|
||||||
|
{ path: 'logs', name: 'AdminLogs', component: () => import('../views/admin/Logs.vue') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -35,12 +35,10 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive } from 'vue'
|
import { ref, reactive } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { systemApi } from '../api'
|
import { systemApi } from '../api'
|
||||||
import { useUserStore } from '../store/user'
|
import { useUserStore } from '../store/user'
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|||||||
@@ -0,0 +1,261 @@
|
|||||||
|
<template>
|
||||||
|
<div class="logs-page">
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="log-info">
|
||||||
|
<span v-if="logInfo" class="info-item">
|
||||||
|
<el-icon><Document /></el-icon>
|
||||||
|
{{ formatSize(logInfo.size) }}
|
||||||
|
</span>
|
||||||
|
<span v-if="logInfo" class="info-item">
|
||||||
|
<el-icon><Clock /></el-icon>
|
||||||
|
{{ formatTime(logInfo.mod_time) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-select v-model="lines" style="width: 120px" @change="fetchLogs">
|
||||||
|
<el-option :value="100" label="100 行" />
|
||||||
|
<el-option :value="500" label="500 行" />
|
||||||
|
<el-option :value="1000" label="1000 行" />
|
||||||
|
<el-option :value="2000" label="2000 行" />
|
||||||
|
</el-select>
|
||||||
|
<el-button @click="fetchLogs" :loading="loading">
|
||||||
|
<el-icon><Refresh /></el-icon>
|
||||||
|
刷新
|
||||||
|
</el-button>
|
||||||
|
<el-popconfirm title="确定清空所有日志?" confirm-button-text="确定" cancel-button-text="取消" @confirm="clearAllLogs">
|
||||||
|
<template #reference>
|
||||||
|
<el-button type="danger" :loading="clearing">
|
||||||
|
<el-icon><Delete /></el-icon>
|
||||||
|
清空
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="log-card">
|
||||||
|
<div class="log-content-wrapper">
|
||||||
|
<pre ref="logContainer" class="log-content" v-html="highlightedContent"></pre>
|
||||||
|
<div v-if="loading" class="log-loading">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Document, Clock, Refresh, Delete, Loading } from '@element-plus/icons-vue'
|
||||||
|
import { adminApi } from '../../api'
|
||||||
|
|
||||||
|
const lines = ref(500)
|
||||||
|
const content = ref('')
|
||||||
|
const logInfo = ref<{ size: number; mod_time: string } | null>(null)
|
||||||
|
const loading = ref(false)
|
||||||
|
const clearing = ref(false)
|
||||||
|
const logContainer = ref<HTMLPreElement | null>(null)
|
||||||
|
let timer: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
|
function highlightLine(line: string): string {
|
||||||
|
const escaped = line.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')
|
||||||
|
if (/\bERROR\b/i.test(line)) {
|
||||||
|
return `<span class="log-error">${escaped}</span>`
|
||||||
|
}
|
||||||
|
if (/\bWARN\b/i.test(line)) {
|
||||||
|
return `<span class="log-warn">${escaped}</span>`
|
||||||
|
}
|
||||||
|
if (/BepUsdt/i.test(line)) {
|
||||||
|
return `<span class="log-bepusdt">${escaped}</span>`
|
||||||
|
}
|
||||||
|
return escaped
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightedContent = computed(() => {
|
||||||
|
if (!content.value) return ''
|
||||||
|
return content.value.split('\n').map(highlightLine).join('\n')
|
||||||
|
})
|
||||||
|
|
||||||
|
function formatSize(bytes: number): string {
|
||||||
|
if (bytes < 1024) return bytes + ' B'
|
||||||
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'
|
||||||
|
return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTime(t: string): string {
|
||||||
|
if (!t) return '-'
|
||||||
|
const d = new Date(t)
|
||||||
|
return d.toLocaleString('zh-CN')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLogs() {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await adminApi.getLogs(lines.value)
|
||||||
|
content.value = res.data?.content || ''
|
||||||
|
await nextTick()
|
||||||
|
scrollToBottom()
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.response?.data?.error || '获取日志失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLogInfo() {
|
||||||
|
try {
|
||||||
|
const res: any = await adminApi.getLogInfo()
|
||||||
|
logInfo.value = res.data || null
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function clearAllLogs() {
|
||||||
|
clearing.value = true
|
||||||
|
try {
|
||||||
|
await adminApi.clearLogs()
|
||||||
|
ElMessage.success('日志已清空')
|
||||||
|
content.value = ''
|
||||||
|
await fetchLogInfo()
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.response?.data?.error || '清空失败')
|
||||||
|
} finally {
|
||||||
|
clearing.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function scrollToBottom() {
|
||||||
|
if (logContainer.value) {
|
||||||
|
logContainer.value.scrollTop = logContainer.value.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchLogs()
|
||||||
|
fetchLogInfo()
|
||||||
|
timer = setInterval(() => {
|
||||||
|
fetchLogs()
|
||||||
|
fetchLogInfo()
|
||||||
|
}, 10000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.logs-page {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-card {
|
||||||
|
background: #2d2d44;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-content-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-content {
|
||||||
|
background: #1a1a2e;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-family: Consolas, Monaco, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 0;
|
||||||
|
max-height: calc(100vh - 200px);
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-error {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-warn {
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-bepusdt {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select .el-input__wrapper) {
|
||||||
|
background: rgba(255, 255, 255, 0.06);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
border-color: #4e6ef2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-select .el-input__inner) {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -37,7 +37,7 @@ function formatDate(date: string) {
|
|||||||
|
|
||||||
async function fetchLotteries() {
|
async function fetchLotteries() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const res: any = await lotteryApi.list({ page: page.value, page_size: pageSize })
|
const res: any = await lotteryApi.list()
|
||||||
lotteries.value = res.data || []
|
lotteries.value = res.data || []
|
||||||
total.value = res.pagination?.total || res.data?.length || 0
|
total.value = res.pagination?.total || res.data?.length || 0
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const quantity = ref(1)
|
|||||||
|
|
||||||
const productImages = computed(() => {
|
const productImages = computed(() => {
|
||||||
if (!product.value?.images) return []
|
if (!product.value?.images) return []
|
||||||
return product.value.images.split(',').map((s: string) => s.trim()).filter(Boolean).map(url => getImageUrl(url))
|
return product.value.images.split(',').map((s: string) => s.trim()).filter(Boolean).map((url: string) => getImageUrl(url))
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
|
|||||||
@@ -1,219 +1,25 @@
|
|||||||
sale | 2026/05/25 05:13:32 Database connected successfully (driver: sqlite)
|
sale | 2026/05/28 00:21:55 /app/backend/internal/api/handlers/order.go:301
|
||||||
sale |
|
sale | [0.888ms] [rows:1] SELECT * FROM `orders` WHERE `orders`.`id` = 8 AND `orders`.`deleted_at` IS NULL AND `orders`.`id` = 8 ORDER BY `orders`.`id` LIMIT 1
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
sale | [GIN] 2026/05/28 - 00:21:55 | 201 | 6.616127ms | 156.245.232.134 | POST "/api/orders"
|
||||||
sale | [0.273ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="users"
|
sale |
|
||||||
sale |
|
sale | 2026/05/28 00:21:56 /app/backend/internal/api/handlers/order.go:24
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
sale | [1.598ms] [rows:0] SELECT * FROM `carts` WHERE user_id = 1
|
||||||
sale | [2.703ms] [rows:0] CREATE TABLE `users` (`id` integer PRIMARY KEY AUTOINCREMENT,`username` text NOT NULL,`email` text NOT NULL,`password_hash` text NOT NULL,`role` text NOT NULL DEFAULT "user",`purchase_credits` integer DEFAULT 0,`invite_code` text,`referred_by` integer,`email_verified` numeric DEFAULT false,`is_active` numeric DEFAULT true,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime)
|
sale | [GIN] 2026/05/28 - 00:21:56 | 200 | 1.758422ms | 156.245.232.134 | GET "/api/cart"
|
||||||
sale |
|
sale |
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
sale | 2026/05/28 00:21:56 /app/backend/internal/api/handlers/auth.go:323
|
||||||
sale | [2.166ms] [rows:0] CREATE INDEX `idx_users_deleted_at` ON `users`(`deleted_at`)
|
sale | [0.282ms] [rows:1] SELECT count(*) FROM `users` WHERE role = "admin" AND `users`.`deleted_at` IS NULL
|
||||||
sale |
|
sale | [GIN] 2026/05/28 - 00:21:56 | 200 | 391.749µs | 156.245.232.134 | GET "/api/installed"
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
sale | [GIN] 2026/05/28 - 00:21:56 | 200 | 366.199µs | 156.245.232.134 | GET "/assets/Checkout-SK8MjkR1.js"
|
||||||
sale | [1.760ms] [rows:0] CREATE UNIQUE INDEX `idx_users_invite_code` ON `users`(`invite_code`)
|
sale | [GIN] 2026/05/28 - 00:21:56 | 200 | 127.963µs | 156.245.232.134 | GET "/assets/Checkout-BJu99NHn.css"
|
||||||
sale |
|
sale |
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
sale | 2026/05/28 00:21:57 /app/backend/internal/api/handlers/payment.go:42
|
||||||
sale | [2.411ms] [rows:0] CREATE UNIQUE INDEX `idx_users_email` ON `users`(`email`)
|
sale | [0.547ms] [rows:1] SELECT * FROM `orders` WHERE (id = 8 AND user_id = 1) AND `orders`.`deleted_at` IS NULL ORDER BY `orders`.`id` LIMIT 1
|
||||||
sale |
|
sale |
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
sale | 2026/05/28 00:21:57 /app/backend/internal/api/handlers/payment.go:53
|
||||||
sale | [2.077ms] [rows:0] CREATE UNIQUE INDEX `idx_users_username` ON `users`(`username`)
|
sale | [0.327ms] [rows:1] SELECT * FROM `payment_channels` WHERE (is_enabled = true AND type = "bepusdt") AND `payment_channels`.`deleted_at` IS NULL ORDER BY sort_order ASC
|
||||||
sale |
|
sale | 2026/05/28 00:21:57 [BepUsdt] signature params: map[amount:100.00 notify_url:https://waiguoyan.com/api/payment/bepusdt/notify order_id:8 redirect_url:https://waiguoyan.com/orders/8 trade_type:usdt.trc20]
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
sale | 2026/05/28 00:21:57 [BepUsdt] signature: b13689e09dd763327e15d20290f328e4, token: DF944A644A32C96F0C5D9CB0D71A9350
|
||||||
sale | [0.049ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="categories"
|
sale | 2026/05/28 00:21:57 [BepUsdt] request body: {"trade_type":"usdt.trc20","order_id":"8","amount":100,"notify_url":"https://waiguoyan.com/api/payment/bepusdt/notify","redirect_url":"https://waiguoyan.com/orders/8","signature":"b13689e09dd763327e15d20290f328e4"}
|
||||||
sale |
|
sale | 2026/05/28 00:21:57 [BepUsdt] response: {"message":"签名错误","status_code":400}
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
sale | [GIN] 2026/05/28 - 00:21:57 | 500 | 505.695187ms | 156.245.232.134 | POST "/api/orders/8/pay"
|
||||||
sale | [1.797ms] [rows:0] CREATE TABLE `categories` (`id` integer PRIMARY KEY AUTOINCREMENT,`name` text NOT NULL,`description` text,`parent_id` integer,`min_amount` real,`max_amount` real,`min_quantity` integer,`max_quantity` integer,`min_weight` real,`max_weight` real,`sort_order` integer DEFAULT 0,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,CONSTRAINT `fk_categories_children` FOREIGN KEY (`parent_id`) REFERENCES `categories`(`id`))
|
root@super:/opt/ace/compose/sale#
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.936ms] [rows:0] CREATE INDEX `idx_categories_deleted_at` ON `categories`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.103ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="brands"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.034ms] [rows:0] CREATE TABLE `brands` (`id` integer PRIMARY KEY AUTOINCREMENT,`name` text NOT NULL,`description` text,`logo` text,`sort_order` integer DEFAULT 0,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.806ms] [rows:0] CREATE INDEX `idx_brands_deleted_at` ON `brands`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.792ms] [rows:0] CREATE UNIQUE INDEX `idx_brands_name` ON `brands`(`name`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.038ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="products"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.787ms] [rows:0] CREATE TABLE `products` (`id` integer PRIMARY KEY AUTOINCREMENT,`name` text NOT NULL,`description` text,`price` decimal(10,2) NOT NULL,`min_purchase` integer DEFAULT 1,`max_purchase` integer,`min_weight` real,`max_weight` real,`min_amount` real,`max_amount` real,`require_credit` numeric DEFAULT false,`credit_cost` integer DEFAULT 0,`credit_reward` integer DEFAULT 0,`images` text,`is_active` numeric DEFAULT true,`brand_id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,CONSTRAINT `fk_products_brand` FOREIGN KEY (`brand_id`) REFERENCES `brands`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.664ms] [rows:0] CREATE INDEX `idx_products_deleted_at` ON `products`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.032ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="product_categories"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.803ms] [rows:0] CREATE TABLE `product_categories` (`product_id` integer,`category_id` integer,PRIMARY KEY (`product_id`,`category_id`),CONSTRAINT `fk_product_categories_product` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`),CONSTRAINT `fk_product_categories_category` FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.031ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="product_custom_fields"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.133ms] [rows:0] CREATE TABLE `product_custom_fields` (`id` integer PRIMARY KEY AUTOINCREMENT,`product_id` integer NOT NULL,`field_name` text NOT NULL,`field_value` text,`created_at` datetime,CONSTRAINT `fk_products_custom_fields` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.875ms] [rows:0] CREATE INDEX `idx_product_custom_fields_product_id` ON `product_custom_fields`(`product_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.031ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="inventory"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.744ms] [rows:0] CREATE TABLE `inventory` (`id` integer PRIMARY KEY AUTOINCREMENT,`product_id` integer NOT NULL,`supplier_id` integer NOT NULL,`quantity` integer DEFAULT 0,`updated_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.152ms] [rows:0] CREATE INDEX `idx_inventory_supplier_id` ON `inventory`(`supplier_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.952ms] [rows:0] CREATE INDEX `idx_inventory_product_id` ON `inventory`(`product_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.037ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="carts"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.982ms] [rows:0] CREATE TABLE `carts` (`id` integer PRIMARY KEY AUTOINCREMENT,`user_id` integer NOT NULL,`product_id` integer NOT NULL,`quantity` integer NOT NULL,`created_at` datetime,`updated_at` datetime,CONSTRAINT `fk_carts_product` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.824ms] [rows:0] CREATE INDEX `idx_carts_product_id` ON `carts`(`product_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.857ms] [rows:0] CREATE INDEX `idx_carts_user_id` ON `carts`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.031ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="addresses"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.888ms] [rows:0] CREATE TABLE `addresses` (`id` integer PRIMARY KEY AUTOINCREMENT,`user_id` integer NOT NULL,`name` text NOT NULL,`phone` text NOT NULL,`province` text,`city` text,`district` text,`address` text NOT NULL,`is_default` numeric DEFAULT false,`created_at` datetime,`updated_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.821ms] [rows:0] CREATE INDEX `idx_addresses_user_id` ON `addresses`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.031ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="orders"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.168ms] [rows:0] CREATE TABLE `orders` (`id` integer PRIMARY KEY AUTOINCREMENT,`user_id` integer NOT NULL,`supplier_id` integer,`subtotal` decimal(10,2) NOT NULL,`shipping_fee` decimal(10,2) DEFAULT 0,`service_fee` decimal(10,2) DEFAULT 0,`tax` decimal(10,2) DEFAULT 0,`total_amount` decimal(10,2) NOT NULL,`refund_amount` decimal(10,2),`refund_status` text,`refund_reason` text,`status` text NOT NULL DEFAULT "pending_payment",`shipping_address_id` integer,`tracking_number` text,`shipping_photo` text,`express_photo` text,`customs_photo` text,`payment_method` text,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,CONSTRAINT `fk_orders_shipping_address` FOREIGN KEY (`shipping_address_id`) REFERENCES `addresses`(`id`),CONSTRAINT `fk_orders_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.849ms] [rows:0] CREATE INDEX `idx_orders_deleted_at` ON `orders`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.702ms] [rows:0] CREATE INDEX `idx_orders_supplier_id` ON `orders`(`supplier_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.699ms] [rows:0] CREATE INDEX `idx_orders_user_id` ON `orders`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.039ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="order_items"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.808ms] [rows:0] CREATE TABLE `order_items` (`id` integer PRIMARY KEY AUTOINCREMENT,`order_id` integer NOT NULL,`product_id` integer NOT NULL,`quantity` integer NOT NULL,`price` decimal(10,2) NOT NULL,CONSTRAINT `fk_order_items_product` FOREIGN KEY (`product_id`) REFERENCES `products`(`id`),CONSTRAINT `fk_orders_order_items` FOREIGN KEY (`order_id`) REFERENCES `orders`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.797ms] [rows:0] CREATE INDEX `idx_order_items_product_id` ON `order_items`(`product_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.782ms] [rows:0] CREATE INDEX `idx_order_items_order_id` ON `order_items`(`order_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.033ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="lotteries"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:32 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.053ms] [rows:0] CREATE TABLE `lotteries` (`id` integer PRIMARY KEY AUTOINCREMENT,`name` text NOT NULL,`description` text,`image` text,`start_time` datetime,`end_time` datetime,`cycle` text,`daily_quota` integer,`total_quota` integer,`registration_validity` integer,`is_active` numeric DEFAULT true,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.951ms] [rows:0] CREATE INDEX `idx_lotteries_deleted_at` ON `lotteries`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.037ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="lottery_prizes"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.128ms] [rows:0] CREATE TABLE `lottery_prizes` (`id` integer PRIMARY KEY AUTOINCREMENT,`lottery_id` integer NOT NULL,`name` text NOT NULL,`type` text NOT NULL,`quantity` integer NOT NULL,`weight` integer DEFAULT 1,`credit_reward` integer,`draw_mode` text DEFAULT "random",CONSTRAINT `fk_lotteries_prizes` FOREIGN KEY (`lottery_id`) REFERENCES `lotteries`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.107ms] [rows:0] CREATE INDEX `idx_lottery_prizes_lottery_id` ON `lottery_prizes`(`lottery_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.041ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="lottery_participants"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.759ms] [rows:0] CREATE TABLE `lottery_participants` (`id` integer PRIMARY KEY AUTOINCREMENT,`lottery_id` integer NOT NULL,`user_id` integer NOT NULL,`purchase_weight` integer DEFAULT 0,`registered_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.771ms] [rows:0] CREATE INDEX `idx_lottery_participants_user_id` ON `lottery_participants`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.999ms] [rows:0] CREATE INDEX `idx_lottery_participants_lottery_id` ON `lottery_participants`(`lottery_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.042ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="lottery_winners"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.949ms] [rows:0] CREATE TABLE `lottery_winners` (`id` integer PRIMARY KEY AUTOINCREMENT,`lottery_id` integer NOT NULL,`prize_id` integer NOT NULL,`user_id` integer NOT NULL,`drawn_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.475ms] [rows:0] CREATE INDEX `idx_lottery_winners_user_id` ON `lottery_winners`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.571ms] [rows:0] CREATE INDEX `idx_lottery_winners_prize_id` ON `lottery_winners`(`prize_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [2.174ms] [rows:0] CREATE INDEX `idx_lottery_winners_lottery_id` ON `lottery_winners`(`lottery_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.044ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="tickets"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.120ms] [rows:0] CREATE TABLE `tickets` (`id` integer PRIMARY KEY AUTOINCREMENT,`user_id` integer NOT NULL,`title` text NOT NULL,`content` text NOT NULL,`category` text,`status` text NOT NULL DEFAULT "pending",`assigned_to` integer,`reply` text,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,CONSTRAINT `fk_tickets_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),CONSTRAINT `fk_tickets_assignee` FOREIGN KEY (`assigned_to`) REFERENCES `users`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.795ms] [rows:0] CREATE INDEX `idx_tickets_deleted_at` ON `tickets`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.772ms] [rows:0] CREATE INDEX `idx_tickets_user_id` ON `tickets`(`user_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.035ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="system_settings"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.284ms] [rows:0] CREATE TABLE `system_settings` (`id` integer PRIMARY KEY AUTOINCREMENT,`key` text NOT NULL,`value` text,`updated_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.969ms] [rows:0] CREATE UNIQUE INDEX `idx_system_settings_key` ON `system_settings`(`key`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.034ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="supplier_authorizations"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [1.884ms] [rows:0] CREATE TABLE `supplier_authorizations` (`id` integer PRIMARY KEY AUTOINCREMENT,`supplier_id` integer NOT NULL,`type` text NOT NULL,`ref_id` integer NOT NULL)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.844ms] [rows:0] CREATE INDEX `idx_supplier_authorizations_supplier_id` ON `supplier_authorizations`(`supplier_id`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.038ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="articles"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.719ms] [rows:0] CREATE TABLE `articles` (`id` integer PRIMARY KEY AUTOINCREMENT,`title` text NOT NULL,`content` text NOT NULL,`summary` text,`cover_image` text,`carousel` text,`is_pinned` numeric DEFAULT false,`is_published` numeric DEFAULT true,`sort_order` integer DEFAULT 0,`author_id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,CONSTRAINT `fk_articles_author` FOREIGN KEY (`author_id`) REFERENCES `users`(`id`))
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.838ms] [rows:0] CREATE INDEX `idx_articles_deleted_at` ON `articles`(`deleted_at`)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:32
|
|
||||||
sale | [0.032ms] [rows:-] SELECT count(*) FROM sqlite_master WHERE type='table' AND name="banners"
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /app/backend/internal/utils/database.go:54
|
|
||||||
sale | [2.439ms] [rows:0] CREATE TABLE `banners` (`id` integer PRIMARY KEY AUTOINCREMENT,`title` text NOT NULL,`desc` text,`image` text NOT NULL,`link` text,`button` text,`bg_color` text,`sort_order` integer DEFAULT 0,`is_active` numeric DEFAULT true,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime)
|
|
||||||
sale |
|
|
||||||
sale | 2026/05/25 05:13:33 /go/pkg/mod/github.com/glebarez/sqlite@v1.11.0/migrator.go:274
|
|
||||||
sale | [1.909ms] [rows:0] CREATE INDEX `idx_banners_deleted_at` ON `banners`(`deleted_at`)
|
|
||||||
sale | 2026/05/25 05:13:33 Database migrated successfully
|
|
||||||
sale | 2026/05/25 05:13:33 Server starting on :8080
|
|
||||||
Reference in New Issue
Block a user