🐛 Fixed SQLite time field issue

This commit is contained in:
2025-09-29 00:59:45 +08:00
parent 3077d5a7c5
commit 4d6a4ff79f
10 changed files with 43 additions and 135 deletions

View File

@@ -1,4 +0,0 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
export * from "./models.js";

View File

@@ -1,51 +0,0 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// This file is automatically generated. DO NOT EDIT
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime";
/**
* A Time represents an instant in time with nanosecond precision.
*
* Programs using times should typically store and pass them as values,
* not pointers. That is, time variables and struct fields should be of
* type [time.Time], not *time.Time.
*
* A Time value can be used by multiple goroutines simultaneously except
* that the methods [Time.GobDecode], [Time.UnmarshalBinary], [Time.UnmarshalJSON] and
* [Time.UnmarshalText] are not concurrency-safe.
*
* Time instants can be compared using the [Time.Before], [Time.After], and [Time.Equal] methods.
* The [Time.Sub] method subtracts two instants, producing a [Duration].
* The [Time.Add] method adds a Time and a Duration, producing a Time.
*
* The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC.
* As this time is unlikely to come up in practice, the [Time.IsZero] method gives
* a simple way of detecting a time that has not been initialized explicitly.
*
* Each time has an associated [Location]. The methods [Time.Local], [Time.UTC], and Time.In return a
* Time with a specific Location. Changing the Location of a Time value with
* these methods does not change the actual instant it represents, only the time
* zone in which to interpret it.
*
* Representations of a Time value saved by the [Time.GobEncode], [Time.MarshalBinary], [Time.AppendBinary],
* [Time.MarshalJSON], [Time.MarshalText] and [Time.AppendText] methods store the [Time.Location]'s offset,
* but not the location name. They therefore lose information about Daylight Saving Time.
*
* In addition to the required “wall clock” reading, a Time may contain an optional
* reading of the current process's monotonic clock, to provide additional precision
* for comparison or subtraction.
* See the “Monotonic Clocks” section in the package documentation for details.
*
* Note that the Go == operator compares not just the time instant but also the
* Location and the monotonic clock reading. Therefore, Time values should not
* be used as map or database keys without first guaranteeing that the
* identical Location has been set for all values, which can be achieved
* through use of the UTC or Local method, and that the monotonic clock reading
* has been stripped by setting t = t.Round(0). In general, prefer t.Equal(u)
* to t == u, since t.Equal uses the most accurate comparison available and
* correctly handles the case when only one of its arguments has a monotonic
* clock reading.
*/
export type Time = any;

View File

@@ -5,10 +5,6 @@
// @ts-ignore: Unused imports // @ts-ignore: Unused imports
import {Create as $Create} from "@wailsio/runtime"; import {Create as $Create} from "@wailsio/runtime";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: Unused imports
import * as time$0 from "../../../time/models.js";
/** /**
* AppConfig 应用配置 - 按照前端设置页面分类组织 * AppConfig 应用配置 - 按照前端设置页面分类组织
*/ */
@@ -196,8 +192,8 @@ export class Document {
"id": number; "id": number;
"title": string; "title": string;
"content": string; "content": string;
"createdAt": time$0.Time; "createdAt": string;
"updatedAt": time$0.Time; "updatedAt": string;
"is_deleted": boolean; "is_deleted": boolean;
/** /**
@@ -217,10 +213,10 @@ export class Document {
this["content"] = ""; this["content"] = "";
} }
if (!("createdAt" in $$source)) { if (!("createdAt" in $$source)) {
this["createdAt"] = null; this["createdAt"] = "";
} }
if (!("updatedAt" in $$source)) { if (!("updatedAt" in $$source)) {
this["updatedAt"] = null; this["updatedAt"] = "";
} }
if (!("is_deleted" in $$source)) { if (!("is_deleted" in $$source)) {
this["is_deleted"] = false; this["is_deleted"] = false;
@@ -1143,8 +1139,8 @@ export class Theme {
"type": ThemeType; "type": ThemeType;
"colors": ThemeColorConfig; "colors": ThemeColorConfig;
"isDefault": boolean; "isDefault": boolean;
"createdAt": time$0.Time; "createdAt": string;
"updatedAt": time$0.Time; "updatedAt": string;
/** Creates a new Theme instance. */ /** Creates a new Theme instance. */
constructor($$source: Partial<Theme> = {}) { constructor($$source: Partial<Theme> = {}) {
@@ -1164,10 +1160,10 @@ export class Theme {
this["isDefault"] = false; this["isDefault"] = false;
} }
if (!("createdAt" in $$source)) { if (!("createdAt" in $$source)) {
this["createdAt"] = null; this["createdAt"] = "";
} }
if (!("updatedAt" in $$source)) { if (!("updatedAt" in $$source)) {
this["updatedAt"] = null; this["updatedAt"] = "";
} }
Object.assign(this, $$source); Object.assign(this, $$source);

View File

@@ -472,7 +472,7 @@ watch(() => documentStore.showDocumentSelector, (isOpen) => {
border-radius: 3px; border-radius: 3px;
margin-bottom: 4px; margin-bottom: 4px;
width: 260px; width: 260px;
max-height: 320px; //max-height: 320px;
z-index: 1000; z-index: 1000;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
overflow: hidden; overflow: hidden;

View File

@@ -150,12 +150,12 @@ export const useDocumentStore = defineStore('document', () => {
const doc = documents.value[docId]; const doc = documents.value[docId];
if (doc) { if (doc) {
doc.title = title; doc.title = title;
doc.updatedAt = new Date(); doc.updatedAt = new Date().toISOString();
} }
if (currentDocument.value?.id === docId) { if (currentDocument.value?.id === docId) {
currentDocument.value.title = title; currentDocument.value.title = title;
currentDocument.value.updatedAt = new Date(); currentDocument.value.updatedAt = new Date().toISOString();
} }
return true; return true;

View File

@@ -6,13 +6,13 @@ import (
// Document represents a document in the system // Document represents a document in the system
type Document struct { type Document struct {
ID int64 `json:"id" db:"id"` ID int64 `json:"id" db:"id"`
Title string `json:"title" db:"title"` Title string `json:"title" db:"title"`
Content string `json:"content" db:"content"` Content string `json:"content" db:"content"`
CreatedAt time.Time `json:"createdAt" db:"created_at"` CreatedAt string `json:"createdAt" db:"created_at"`
UpdatedAt time.Time `json:"updatedAt" db:"updated_at"` UpdatedAt string `json:"updatedAt" db:"updated_at"`
IsDeleted bool `json:"is_deleted" db:"is_deleted"` IsDeleted bool `json:"is_deleted" db:"is_deleted"`
IsLocked bool `json:"is_locked" db:"is_locked"` // 锁定标志,锁定的文档无法被删除 IsLocked bool `json:"is_locked" db:"is_locked"` // 锁定标志,锁定的文档无法被删除
} }
// NewDocument 创建新文档 // NewDocument 创建新文档
@@ -21,8 +21,8 @@ func NewDocument(title, content string) *Document {
return &Document{ return &Document{
Title: title, Title: title,
Content: content, Content: content,
CreatedAt: now, CreatedAt: now.String(),
UpdatedAt: now, UpdatedAt: now.String(),
IsDeleted: false, IsDeleted: false,
IsLocked: false, // 默认不锁定 IsLocked: false, // 默认不锁定
} }

View File

@@ -4,7 +4,6 @@ import (
"database/sql/driver" "database/sql/driver"
"encoding/json" "encoding/json"
"fmt" "fmt"
"time"
) )
// ThemeType 主题类型枚举 // ThemeType 主题类型枚举
@@ -58,8 +57,8 @@ type Theme struct {
Type ThemeType `db:"type" json:"type"` Type ThemeType `db:"type" json:"type"`
Colors ThemeColorConfig `db:"colors" json:"colors"` Colors ThemeColorConfig `db:"colors" json:"colors"`
IsDefault bool `db:"is_default" json:"isDefault"` IsDefault bool `db:"is_default" json:"isDefault"`
CreatedAt time.Time `db:"created_at" json:"createdAt"` CreatedAt string `db:"created_at" json:"createdAt"`
UpdatedAt time.Time `db:"updated_at" json:"updatedAt"` UpdatedAt string `db:"updated_at" json:"updatedAt"`
} }
// Value 实现 driver.Valuer 接口,用于将 ThemeColorConfig 存储到数据库 // Value 实现 driver.Valuer 接口,用于将 ThemeColorConfig 存储到数据库

View File

@@ -8,7 +8,6 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"sync" "sync"
"time"
"voidraft/internal/models" "voidraft/internal/models"
"github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/application"
@@ -33,8 +32,8 @@ CREATE TABLE IF NOT EXISTS documents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL, title TEXT NOT NULL,
content TEXT DEFAULT '∞∞∞text-a', content TEXT DEFAULT '∞∞∞text-a',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
is_deleted INTEGER DEFAULT 0, is_deleted INTEGER DEFAULT 0,
is_locked INTEGER DEFAULT 0 is_locked INTEGER DEFAULT 0
)` )`
@@ -46,8 +45,8 @@ CREATE TABLE IF NOT EXISTS extensions (
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
is_default INTEGER NOT NULL DEFAULT 0, is_default INTEGER NOT NULL DEFAULT 0,
config TEXT DEFAULT '{}', config TEXT DEFAULT '{}',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at TEXT DEFAULT CURRENT_TIMESTAMP
)` )`
// Key bindings table // Key bindings table
@@ -59,8 +58,8 @@ CREATE TABLE IF NOT EXISTS key_bindings (
key TEXT NOT NULL, key TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1, enabled INTEGER NOT NULL DEFAULT 1,
is_default INTEGER NOT NULL DEFAULT 0, is_default INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(command, extension) UNIQUE(command, extension)
)` )`
@@ -72,8 +71,8 @@ CREATE TABLE IF NOT EXISTS themes (
type TEXT NOT NULL, type TEXT NOT NULL,
colors TEXT NOT NULL, colors TEXT NOT NULL,
is_default INTEGER NOT NULL DEFAULT 0, is_default INTEGER NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at TEXT DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP,
UNIQUE(type, is_default) UNIQUE(type, is_default)
)` )`
) )
@@ -363,10 +362,6 @@ func getSQLTypeAndDefault(t reflect.Type) (string, string) {
case reflect.String: case reflect.String:
return "TEXT", "''" return "TEXT", "''"
default: default:
// 处理特殊类型
if t == reflect.TypeOf(time.Time{}) {
return "DATETIME", "CURRENT_TIMESTAMP"
}
return "TEXT", "NULL" return "TEXT", "NULL"
} }
} }

View File

@@ -141,15 +141,14 @@ func (ds *DocumentService) GetDocumentByID(id int64) (*models.Document, error) {
} }
doc := &models.Document{} doc := &models.Document{}
var createdAt, updatedAt string
var isDeleted, isLocked int var isDeleted, isLocked int
err := ds.databaseService.db.QueryRow(sqlGetDocumentByID, id).Scan( err := ds.databaseService.db.QueryRow(sqlGetDocumentByID, id).Scan(
&doc.ID, &doc.ID,
&doc.Title, &doc.Title,
&doc.Content, &doc.Content,
&createdAt, &doc.CreatedAt,
&updatedAt, &doc.UpdatedAt,
&isDeleted, &isDeleted,
&isLocked, &isLocked,
) )
@@ -161,14 +160,6 @@ func (ds *DocumentService) GetDocumentByID(id int64) (*models.Document, error) {
return nil, fmt.Errorf("failed to get document by ID: %w", err) return nil, fmt.Errorf("failed to get document by ID: %w", err)
} }
// 转换时间字段
if t, err := time.Parse("2006-01-02 15:04:05", createdAt); err == nil {
doc.CreatedAt = t
}
if t, err := time.Parse("2006-01-02 15:04:05", updatedAt); err == nil {
doc.UpdatedAt = t
}
// 转换布尔字段 // 转换布尔字段
doc.IsDeleted = isDeleted == 1 doc.IsDeleted = isDeleted == 1
doc.IsLocked = isLocked == 1 doc.IsLocked = isLocked == 1
@@ -190,15 +181,15 @@ func (ds *DocumentService) CreateDocument(title string) (*models.Document, error
doc := &models.Document{ doc := &models.Document{
Title: title, Title: title,
Content: "∞∞∞text-a\n", Content: "∞∞∞text-a\n",
CreatedAt: now, CreatedAt: now.String(),
UpdatedAt: now, UpdatedAt: now.String(),
IsDeleted: false, IsDeleted: false,
IsLocked: false, IsLocked: false,
} }
// 执行插入操作 // 执行插入操作
result, err := ds.databaseService.db.Exec(sqlInsertDocument, result, err := ds.databaseService.db.Exec(sqlInsertDocument,
doc.Title, doc.Content, doc.CreatedAt.Format("2006-01-02 15:04:05"), doc.UpdatedAt.Format("2006-01-02 15:04:05")) doc.Title, doc.Content, doc.CreatedAt, doc.UpdatedAt)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create document: %w", err) return nil, fmt.Errorf("failed to create document: %w", err)
} }
@@ -380,14 +371,13 @@ func (ds *DocumentService) ListAllDocumentsMeta() ([]*models.Document, error) {
var documents []*models.Document var documents []*models.Document
for rows.Next() { for rows.Next() {
doc := &models.Document{IsDeleted: false} doc := &models.Document{IsDeleted: false}
var createdAt, updatedAt string
var isLocked int var isLocked int
err := rows.Scan( err := rows.Scan(
&doc.ID, &doc.ID,
&doc.Title, &doc.Title,
&createdAt, &doc.CreatedAt,
&updatedAt, &doc.UpdatedAt,
&isLocked, &isLocked,
) )
@@ -395,14 +385,6 @@ func (ds *DocumentService) ListAllDocumentsMeta() ([]*models.Document, error) {
return nil, fmt.Errorf("failed to scan document row: %w", err) return nil, fmt.Errorf("failed to scan document row: %w", err)
} }
// 转换时间字段
if t, err := time.Parse("2006-01-02 15:04:05", createdAt); err == nil {
doc.CreatedAt = t
}
if t, err := time.Parse("2006-01-02 15:04:05", updatedAt); err == nil {
doc.UpdatedAt = t
}
doc.IsLocked = isLocked == 1 doc.IsLocked = isLocked == 1
documents = append(documents, doc) documents = append(documents, doc)
} }
@@ -432,14 +414,13 @@ func (ds *DocumentService) ListDeletedDocumentsMeta() ([]*models.Document, error
var documents []*models.Document var documents []*models.Document
for rows.Next() { for rows.Next() {
doc := &models.Document{IsDeleted: true} doc := &models.Document{IsDeleted: true}
var createdAt, updatedAt string
var isLocked int var isLocked int
err := rows.Scan( err := rows.Scan(
&doc.ID, &doc.ID,
&doc.Title, &doc.Title,
&createdAt, &doc.CreatedAt,
&updatedAt, &doc.UpdatedAt,
&isLocked, &isLocked,
) )
@@ -447,14 +428,6 @@ func (ds *DocumentService) ListDeletedDocumentsMeta() ([]*models.Document, error
return nil, fmt.Errorf("failed to scan document row: %w", err) return nil, fmt.Errorf("failed to scan document row: %w", err)
} }
// 转换时间字段
if t, err := time.Parse("2006-01-02 15:04:05", createdAt); err == nil {
doc.CreatedAt = t
}
if t, err := time.Parse("2006-01-02 15:04:05", updatedAt); err == nil {
doc.UpdatedAt = t
}
doc.IsLocked = isLocked == 1 doc.IsLocked = isLocked == 1
documents = append(documents, doc) documents = append(documents, doc)
} }

View File

@@ -67,8 +67,8 @@ func (ts *ThemeService) initializeDefaultThemes() error {
Type: models.ThemeTypeDark, Type: models.ThemeTypeDark,
Colors: *models.NewDefaultDarkTheme(), Colors: *models.NewDefaultDarkTheme(),
IsDefault: true, IsDefault: true,
CreatedAt: time.Now(), CreatedAt: time.Now().String(),
UpdatedAt: time.Now(), UpdatedAt: time.Now().String(),
} }
// 创建默认浅色主题 // 创建默认浅色主题
@@ -77,8 +77,8 @@ func (ts *ThemeService) initializeDefaultThemes() error {
Type: models.ThemeTypeLight, Type: models.ThemeTypeLight,
Colors: *models.NewDefaultLightTheme(), Colors: *models.NewDefaultLightTheme(),
IsDefault: true, IsDefault: true,
CreatedAt: time.Now(), CreatedAt: time.Now().String(),
UpdatedAt: time.Now(), UpdatedAt: time.Now().String(),
} }
// 插入默认主题 // 插入默认主题