完成
This commit is contained in:
427
golang-learning/10-projects/02-todo-list/todo/manager.go
Normal file
427
golang-learning/10-projects/02-todo-list/todo/manager.go
Normal file
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
manager.go - 任务管理器
|
||||
实现了任务的增删改查和业务逻辑
|
||||
*/
|
||||
|
||||
package todo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Statistics 统计信息
|
||||
type Statistics struct {
|
||||
Total int `json:"total"` // 总任务数
|
||||
Completed int `json:"completed"` // 已完成任务数
|
||||
Pending int `json:"pending"` // 待完成任务数
|
||||
CompletionRate float64 `json:"completion_rate"` // 完成率
|
||||
ByPriority map[string]int `json:"by_priority"` // 按优先级统计
|
||||
ByStatus map[string]int `json:"by_status"` // 按状态统计
|
||||
AverageCompletionTime time.Duration `json:"average_completion_time"` // 平均完成时间
|
||||
}
|
||||
|
||||
// Manager 任务管理器
|
||||
type Manager struct {
|
||||
tasks []Task
|
||||
nextID int
|
||||
storage Storage
|
||||
}
|
||||
|
||||
// NewManager 创建新的任务管理器
|
||||
func NewManager(dataFile string) (*Manager, error) {
|
||||
storage, err := NewJSONStorage(dataFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建存储失败: %v", err)
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
tasks: make([]Task, 0),
|
||||
nextID: 1,
|
||||
storage: storage,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Load 从存储加载任务
|
||||
func (m *Manager) Load() error {
|
||||
tasks, err := m.storage.Load()
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载任务失败: %v", err)
|
||||
}
|
||||
|
||||
m.tasks = tasks
|
||||
|
||||
// 更新下一个ID
|
||||
maxID := 0
|
||||
for _, task := range m.tasks {
|
||||
if task.ID > maxID {
|
||||
maxID = task.ID
|
||||
}
|
||||
}
|
||||
m.nextID = maxID + 1
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save 保存任务到存储
|
||||
func (m *Manager) Save() error {
|
||||
return m.storage.Save(m.tasks)
|
||||
}
|
||||
|
||||
// AddTask 添加新任务
|
||||
func (m *Manager) AddTask(content string) (*Task, error) {
|
||||
content = strings.TrimSpace(content)
|
||||
if content == "" {
|
||||
return nil, fmt.Errorf("任务内容不能为空")
|
||||
}
|
||||
|
||||
task := NewTask(m.nextID, content)
|
||||
m.tasks = append(m.tasks, *task)
|
||||
m.nextID++
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// GetTasks 获取所有任务
|
||||
func (m *Manager) GetTasks() []Task {
|
||||
// 返回副本以防止外部修改
|
||||
tasks := make([]Task, len(m.tasks))
|
||||
copy(tasks, m.tasks)
|
||||
|
||||
// 排序任务
|
||||
sort.Sort(TaskList(tasks))
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
// GetTask 根据ID获取任务
|
||||
func (m *Manager) GetTask(id int) (*Task, error) {
|
||||
for i, task := range m.tasks {
|
||||
if task.ID == id {
|
||||
return &m.tasks[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("任务不存在: ID %d", id)
|
||||
}
|
||||
|
||||
// UpdateTask 更新任务内容
|
||||
func (m *Manager) UpdateTask(id int, content string) (*Task, error) {
|
||||
content = strings.TrimSpace(content)
|
||||
if content == "" {
|
||||
return nil, fmt.Errorf("任务内容不能为空")
|
||||
}
|
||||
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.Update(content)
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// CompleteTask 标记任务完成
|
||||
func (m *Manager) CompleteTask(id int) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if task.Completed {
|
||||
return nil, fmt.Errorf("任务已经完成")
|
||||
}
|
||||
|
||||
task.Complete()
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// UncompleteTask 标记任务未完成
|
||||
func (m *Manager) UncompleteTask(id int) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !task.Completed {
|
||||
return nil, fmt.Errorf("任务尚未完成")
|
||||
}
|
||||
|
||||
task.Uncomplete()
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// DeleteTask 删除任务
|
||||
func (m *Manager) DeleteTask(id int) (*Task, error) {
|
||||
for i, task := range m.tasks {
|
||||
if task.ID == id {
|
||||
// 创建任务副本用于返回
|
||||
deletedTask := task
|
||||
|
||||
// 从切片中删除任务
|
||||
m.tasks = append(m.tasks[:i], m.tasks[i+1:]...)
|
||||
|
||||
return &deletedTask, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("任务不存在: ID %d", id)
|
||||
}
|
||||
|
||||
// SearchTasks 搜索任务
|
||||
func (m *Manager) SearchTasks(keyword string) []Task {
|
||||
keyword = strings.TrimSpace(keyword)
|
||||
if keyword == "" {
|
||||
return []Task{}
|
||||
}
|
||||
|
||||
var results []Task
|
||||
for _, task := range m.tasks {
|
||||
if m.matchesKeyword(task, keyword) {
|
||||
results = append(results, task)
|
||||
}
|
||||
}
|
||||
|
||||
// 排序结果
|
||||
sort.Sort(TaskList(results))
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
// matchesKeyword 检查任务是否匹配关键词
|
||||
func (m *Manager) matchesKeyword(task Task, keyword string) bool {
|
||||
keyword = strings.ToLower(keyword)
|
||||
|
||||
// 检查任务内容
|
||||
if strings.Contains(strings.ToLower(task.Content), keyword) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查任务描述
|
||||
if strings.Contains(strings.ToLower(task.Description), keyword) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查标签
|
||||
for _, tag := range task.Tags {
|
||||
if strings.Contains(strings.ToLower(tag), keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// GetTasksByStatus 根据状态获取任务
|
||||
func (m *Manager) GetTasksByStatus(completed bool) []Task {
|
||||
var results []Task
|
||||
for _, task := range m.tasks {
|
||||
if task.Completed == completed {
|
||||
results = append(results, task)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(TaskList(results))
|
||||
return results
|
||||
}
|
||||
|
||||
// GetTasksByPriority 根据优先级获取任务
|
||||
func (m *Manager) GetTasksByPriority(priority Priority) []Task {
|
||||
var results []Task
|
||||
for _, task := range m.tasks {
|
||||
if task.Priority == priority {
|
||||
results = append(results, task)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(TaskList(results))
|
||||
return results
|
||||
}
|
||||
|
||||
// SetTaskPriority 设置任务优先级
|
||||
func (m *Manager) SetTaskPriority(id int, priority Priority) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.SetPriority(priority)
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// AddTaskTag 为任务添加标签
|
||||
func (m *Manager) AddTaskTag(id int, tag string) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.AddTag(tag)
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// RemoveTaskTag 移除任务标签
|
||||
func (m *Manager) RemoveTaskTag(id int, tag string) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.RemoveTag(tag)
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// SetTaskDescription 设置任务描述
|
||||
func (m *Manager) SetTaskDescription(id int, description string) (*Task, error) {
|
||||
task, err := m.GetTask(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task.SetDescription(description)
|
||||
return task, nil
|
||||
}
|
||||
|
||||
// GetStatistics 获取统计信息
|
||||
func (m *Manager) GetStatistics() Statistics {
|
||||
stats := Statistics{
|
||||
Total: len(m.tasks),
|
||||
Completed: 0,
|
||||
Pending: 0,
|
||||
ByPriority: make(map[string]int),
|
||||
ByStatus: make(map[string]int),
|
||||
}
|
||||
|
||||
var totalCompletionTime time.Duration
|
||||
completedCount := 0
|
||||
|
||||
for _, task := range m.tasks {
|
||||
// 统计完成状态
|
||||
if task.Completed {
|
||||
stats.Completed++
|
||||
if task.CompletedAt != nil {
|
||||
totalCompletionTime += task.CompletionTime()
|
||||
completedCount++
|
||||
}
|
||||
} else {
|
||||
stats.Pending++
|
||||
}
|
||||
|
||||
// 统计优先级
|
||||
priorityStr := task.Priority.String()
|
||||
stats.ByPriority[priorityStr]++
|
||||
|
||||
// 统计状态
|
||||
if task.Completed {
|
||||
stats.ByStatus["已完成"]++
|
||||
} else {
|
||||
stats.ByStatus["待完成"]++
|
||||
}
|
||||
}
|
||||
|
||||
// 计算完成率
|
||||
if stats.Total > 0 {
|
||||
stats.CompletionRate = float64(stats.Completed) / float64(stats.Total) * 100
|
||||
}
|
||||
|
||||
// 计算平均完成时间
|
||||
if completedCount > 0 {
|
||||
stats.AverageCompletionTime = totalCompletionTime / time.Duration(completedCount)
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
// ClearCompleted 清除所有已完成的任务
|
||||
func (m *Manager) ClearCompleted() int {
|
||||
var remaining []Task
|
||||
clearedCount := 0
|
||||
|
||||
for _, task := range m.tasks {
|
||||
if task.Completed {
|
||||
clearedCount++
|
||||
} else {
|
||||
remaining = append(remaining, task)
|
||||
}
|
||||
}
|
||||
|
||||
m.tasks = remaining
|
||||
return clearedCount
|
||||
}
|
||||
|
||||
// ClearAll 清除所有任务
|
||||
func (m *Manager) ClearAll() int {
|
||||
count := len(m.tasks)
|
||||
m.tasks = make([]Task, 0)
|
||||
m.nextID = 1
|
||||
return count
|
||||
}
|
||||
|
||||
// GetTaskCount 获取任务数量
|
||||
func (m *Manager) GetTaskCount() int {
|
||||
return len(m.tasks)
|
||||
}
|
||||
|
||||
// GetCompletedCount 获取已完成任务数量
|
||||
func (m *Manager) GetCompletedCount() int {
|
||||
count := 0
|
||||
for _, task := range m.tasks {
|
||||
if task.Completed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// GetPendingCount 获取待完成任务数量
|
||||
func (m *Manager) GetPendingCount() int {
|
||||
count := 0
|
||||
for _, task := range m.tasks {
|
||||
if !task.Completed {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// ValidateAllTasks 验证所有任务的有效性
|
||||
func (m *Manager) ValidateAllTasks() []error {
|
||||
var errors []error
|
||||
for i, task := range m.tasks {
|
||||
if err := task.Validate(); err != nil {
|
||||
errors = append(errors, fmt.Errorf("任务 %d: %v", i, err))
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
// ExportTasks 导出任务(可以扩展为不同格式)
|
||||
func (m *Manager) ExportTasks() []Task {
|
||||
tasks := make([]Task, len(m.tasks))
|
||||
copy(tasks, m.tasks)
|
||||
return tasks
|
||||
}
|
||||
|
||||
// ImportTasks 导入任务
|
||||
func (m *Manager) ImportTasks(tasks []Task) error {
|
||||
// 验证导入的任务
|
||||
for i, task := range tasks {
|
||||
if err := task.Validate(); err != nil {
|
||||
return fmt.Errorf("导入任务 %d 验证失败: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 更新任务列表
|
||||
m.tasks = append(m.tasks, tasks...)
|
||||
|
||||
// 更新下一个ID
|
||||
maxID := m.nextID - 1
|
||||
for _, task := range tasks {
|
||||
if task.ID > maxID {
|
||||
maxID = task.ID
|
||||
}
|
||||
}
|
||||
m.nextID = maxID + 1
|
||||
|
||||
return nil
|
||||
}
|
308
golang-learning/10-projects/02-todo-list/todo/storage.go
Normal file
308
golang-learning/10-projects/02-todo-list/todo/storage.go
Normal file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
storage.go - 数据存储
|
||||
实现了任务数据的持久化存储
|
||||
*/
|
||||
|
||||
package todo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Storage 存储接口
|
||||
type Storage interface {
|
||||
Load() ([]Task, error)
|
||||
Save(tasks []Task) error
|
||||
}
|
||||
|
||||
// JSONStorage JSON文件存储实现
|
||||
type JSONStorage struct {
|
||||
filePath string
|
||||
}
|
||||
|
||||
// NewJSONStorage 创建新的JSON存储
|
||||
func NewJSONStorage(filePath string) (*JSONStorage, error) {
|
||||
if filePath == "" {
|
||||
return nil, fmt.Errorf("文件路径不能为空")
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(filePath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("创建目录失败: %v", err)
|
||||
}
|
||||
|
||||
return &JSONStorage{
|
||||
filePath: filePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Load 从JSON文件加载任务
|
||||
func (s *JSONStorage) Load() ([]Task, error) {
|
||||
// 检查文件是否存在
|
||||
if _, err := os.Stat(s.filePath); os.IsNotExist(err) {
|
||||
// 文件不存在,返回空任务列表
|
||||
return []Task{}, nil
|
||||
}
|
||||
|
||||
// 读取文件内容
|
||||
data, err := ioutil.ReadFile(s.filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 如果文件为空,返回空任务列表
|
||||
if len(data) == 0 {
|
||||
return []Task{}, nil
|
||||
}
|
||||
|
||||
// 解析JSON数据
|
||||
var tasks []Task
|
||||
if err := json.Unmarshal(data, &tasks); err != nil {
|
||||
return nil, fmt.Errorf("解析JSON数据失败: %v", err)
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// Save 保存任务到JSON文件
|
||||
func (s *JSONStorage) Save(tasks []Task) error {
|
||||
// 序列化任务数据
|
||||
data, err := json.MarshalIndent(tasks, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化任务数据失败: %v", err)
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
if err := ioutil.WriteFile(s.filePath, data, 0644); err != nil {
|
||||
return fmt.Errorf("写入文件失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFilePath 获取文件路径
|
||||
func (s *JSONStorage) GetFilePath() string {
|
||||
return s.filePath
|
||||
}
|
||||
|
||||
// FileExists 检查文件是否存在
|
||||
func (s *JSONStorage) FileExists() bool {
|
||||
_, err := os.Stat(s.filePath)
|
||||
return !os.IsNotExist(err)
|
||||
}
|
||||
|
||||
// GetFileSize 获取文件大小
|
||||
func (s *JSONStorage) GetFileSize() (int64, error) {
|
||||
info, err := os.Stat(s.filePath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Size(), nil
|
||||
}
|
||||
|
||||
// Backup 备份数据文件
|
||||
func (s *JSONStorage) Backup(backupPath string) error {
|
||||
if !s.FileExists() {
|
||||
return fmt.Errorf("源文件不存在")
|
||||
}
|
||||
|
||||
// 读取源文件
|
||||
data, err := ioutil.ReadFile(s.filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取源文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 确保备份目录存在
|
||||
dir := filepath.Dir(backupPath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return fmt.Errorf("创建备份目录失败: %v", err)
|
||||
}
|
||||
|
||||
// 写入备份文件
|
||||
if err := ioutil.WriteFile(backupPath, data, 0644); err != nil {
|
||||
return fmt.Errorf("写入备份文件失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Restore 从备份恢复数据
|
||||
func (s *JSONStorage) Restore(backupPath string) error {
|
||||
// 检查备份文件是否存在
|
||||
if _, err := os.Stat(backupPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("备份文件不存在")
|
||||
}
|
||||
|
||||
// 读取备份文件
|
||||
data, err := ioutil.ReadFile(backupPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("读取备份文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证备份数据
|
||||
var tasks []Task
|
||||
if err := json.Unmarshal(data, &tasks); err != nil {
|
||||
return fmt.Errorf("备份数据格式无效: %v", err)
|
||||
}
|
||||
|
||||
// 写入主文件
|
||||
if err := ioutil.WriteFile(s.filePath, data, 0644); err != nil {
|
||||
return fmt.Errorf("恢复数据失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear 清空数据文件
|
||||
func (s *JSONStorage) Clear() error {
|
||||
return s.Save([]Task{})
|
||||
}
|
||||
|
||||
// MemoryStorage 内存存储实现(用于测试)
|
||||
type MemoryStorage struct {
|
||||
tasks []Task
|
||||
}
|
||||
|
||||
// NewMemoryStorage 创建新的内存存储
|
||||
func NewMemoryStorage() *MemoryStorage {
|
||||
return &MemoryStorage{
|
||||
tasks: make([]Task, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Load 从内存加载任务
|
||||
func (s *MemoryStorage) Load() ([]Task, error) {
|
||||
// 返回任务副本
|
||||
tasks := make([]Task, len(s.tasks))
|
||||
copy(tasks, s.tasks)
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
// Save 保存任务到内存
|
||||
func (s *MemoryStorage) Save(tasks []Task) error {
|
||||
// 保存任务副本
|
||||
s.tasks = make([]Task, len(tasks))
|
||||
copy(s.tasks, tasks)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear 清空内存中的任务
|
||||
func (s *MemoryStorage) Clear() {
|
||||
s.tasks = make([]Task, 0)
|
||||
}
|
||||
|
||||
// GetTasks 获取内存中的任务(用于测试)
|
||||
func (s *MemoryStorage) GetTasks() []Task {
|
||||
tasks := make([]Task, len(s.tasks))
|
||||
copy(tasks, s.tasks)
|
||||
return tasks
|
||||
}
|
||||
|
||||
// CSVStorage CSV文件存储实现(扩展功能)
|
||||
type CSVStorage struct {
|
||||
filePath string
|
||||
}
|
||||
|
||||
// NewCSVStorage 创建新的CSV存储
|
||||
func NewCSVStorage(filePath string) (*CSVStorage, error) {
|
||||
if filePath == "" {
|
||||
return nil, fmt.Errorf("文件路径不能为空")
|
||||
}
|
||||
|
||||
// 确保目录存在
|
||||
dir := filepath.Dir(filePath)
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, fmt.Errorf("创建目录失败: %v", err)
|
||||
}
|
||||
|
||||
return &CSVStorage{
|
||||
filePath: filePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Load 从CSV文件加载任务(简化实现)
|
||||
func (s *CSVStorage) Load() ([]Task, error) {
|
||||
// 这里可以实现CSV解析逻辑
|
||||
// 为了简化,暂时返回空列表
|
||||
return []Task{}, nil
|
||||
}
|
||||
|
||||
// Save 保存任务到CSV文件(简化实现)
|
||||
func (s *CSVStorage) Save(tasks []Task) error {
|
||||
// 这里可以实现CSV写入逻辑
|
||||
// 为了简化,暂时不做任何操作
|
||||
return nil
|
||||
}
|
||||
|
||||
// StorageManager 存储管理器
|
||||
type StorageManager struct {
|
||||
storages map[string]Storage
|
||||
current string
|
||||
}
|
||||
|
||||
// NewStorageManager 创建新的存储管理器
|
||||
func NewStorageManager() *StorageManager {
|
||||
return &StorageManager{
|
||||
storages: make(map[string]Storage),
|
||||
}
|
||||
}
|
||||
|
||||
// AddStorage 添加存储实现
|
||||
func (sm *StorageManager) AddStorage(name string, storage Storage) {
|
||||
sm.storages[name] = storage
|
||||
}
|
||||
|
||||
// SetCurrent 设置当前使用的存储
|
||||
func (sm *StorageManager) SetCurrent(name string) error {
|
||||
if _, exists := sm.storages[name]; !exists {
|
||||
return fmt.Errorf("存储 '%s' 不存在", name)
|
||||
}
|
||||
sm.current = name
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCurrent 获取当前存储
|
||||
func (sm *StorageManager) GetCurrent() (Storage, error) {
|
||||
if sm.current == "" {
|
||||
return nil, fmt.Errorf("未设置当前存储")
|
||||
}
|
||||
|
||||
storage, exists := sm.storages[sm.current]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("当前存储 '%s' 不存在", sm.current)
|
||||
}
|
||||
|
||||
return storage, nil
|
||||
}
|
||||
|
||||
// ListStorages 列出所有可用的存储
|
||||
func (sm *StorageManager) ListStorages() []string {
|
||||
var names []string
|
||||
for name := range sm.storages {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Load 使用当前存储加载数据
|
||||
func (sm *StorageManager) Load() ([]Task, error) {
|
||||
storage, err := sm.GetCurrent()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return storage.Load()
|
||||
}
|
||||
|
||||
// Save 使用当前存储保存数据
|
||||
func (sm *StorageManager) Save(tasks []Task) error {
|
||||
storage, err := sm.GetCurrent()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return storage.Save(tasks)
|
||||
}
|
356
golang-learning/10-projects/02-todo-list/todo/todo.go
Normal file
356
golang-learning/10-projects/02-todo-list/todo/todo.go
Normal file
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
todo.go - 待办事项数据结构
|
||||
定义了任务的基本数据结构和相关方法
|
||||
*/
|
||||
|
||||
package todo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Priority 任务优先级
|
||||
type Priority int
|
||||
|
||||
const (
|
||||
Low Priority = iota
|
||||
Medium
|
||||
High
|
||||
Urgent
|
||||
)
|
||||
|
||||
// String 返回优先级的字符串表示
|
||||
func (p Priority) String() string {
|
||||
switch p {
|
||||
case Low:
|
||||
return "低"
|
||||
case Medium:
|
||||
return "中"
|
||||
case High:
|
||||
return "高"
|
||||
case Urgent:
|
||||
return "紧急"
|
||||
default:
|
||||
return "未知"
|
||||
}
|
||||
}
|
||||
|
||||
// Task 表示一个待办事项
|
||||
type Task struct {
|
||||
ID int `json:"id"` // 任务ID
|
||||
Content string `json:"content"` // 任务内容
|
||||
Completed bool `json:"completed"` // 是否完成
|
||||
Priority Priority `json:"priority"` // 优先级
|
||||
CreatedAt time.Time `json:"created_at"` // 创建时间
|
||||
UpdatedAt time.Time `json:"updated_at"` // 更新时间
|
||||
CompletedAt *time.Time `json:"completed_at,omitempty"` // 完成时间
|
||||
Tags []string `json:"tags,omitempty"` // 标签
|
||||
Description string `json:"description,omitempty"` // 详细描述
|
||||
}
|
||||
|
||||
// NewTask 创建新的任务
|
||||
func NewTask(id int, content string) *Task {
|
||||
now := time.Now()
|
||||
return &Task{
|
||||
ID: id,
|
||||
Content: content,
|
||||
Completed: false,
|
||||
Priority: Medium,
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Tags: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// Complete 标记任务为完成
|
||||
func (t *Task) Complete() {
|
||||
if !t.Completed {
|
||||
t.Completed = true
|
||||
now := time.Now()
|
||||
t.CompletedAt = &now
|
||||
t.UpdatedAt = now
|
||||
}
|
||||
}
|
||||
|
||||
// Uncomplete 标记任务为未完成
|
||||
func (t *Task) Uncomplete() {
|
||||
if t.Completed {
|
||||
t.Completed = false
|
||||
t.CompletedAt = nil
|
||||
t.UpdatedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// Update 更新任务内容
|
||||
func (t *Task) Update(content string) {
|
||||
if content != "" && content != t.Content {
|
||||
t.Content = content
|
||||
t.UpdatedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// SetPriority 设置任务优先级
|
||||
func (t *Task) SetPriority(priority Priority) {
|
||||
if priority != t.Priority {
|
||||
t.Priority = priority
|
||||
t.UpdatedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// AddTag 添加标签
|
||||
func (t *Task) AddTag(tag string) {
|
||||
if tag == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// 检查标签是否已存在
|
||||
for _, existingTag := range t.Tags {
|
||||
if existingTag == tag {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t.Tags = append(t.Tags, tag)
|
||||
t.UpdatedAt = time.Now()
|
||||
}
|
||||
|
||||
// RemoveTag 移除标签
|
||||
func (t *Task) RemoveTag(tag string) {
|
||||
for i, existingTag := range t.Tags {
|
||||
if existingTag == tag {
|
||||
t.Tags = append(t.Tags[:i], t.Tags[i+1:]...)
|
||||
t.UpdatedAt = time.Now()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HasTag 检查是否包含指定标签
|
||||
func (t *Task) HasTag(tag string) bool {
|
||||
for _, existingTag := range t.Tags {
|
||||
if existingTag == tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetDescription 设置任务描述
|
||||
func (t *Task) SetDescription(description string) {
|
||||
if description != t.Description {
|
||||
t.Description = description
|
||||
t.UpdatedAt = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// String 返回任务的字符串表示
|
||||
func (t *Task) String() string {
|
||||
status := "⏳"
|
||||
if t.Completed {
|
||||
status = "✅"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%d] %s %s (优先级: %s)",
|
||||
t.ID, status, t.Content, t.Priority)
|
||||
}
|
||||
|
||||
// DetailedString 返回任务的详细字符串表示
|
||||
func (t *Task) DetailedString() string {
|
||||
status := "待完成"
|
||||
if t.Completed {
|
||||
status = "已完成"
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("任务 #%d\n", t.ID)
|
||||
result += fmt.Sprintf("内容: %s\n", t.Content)
|
||||
result += fmt.Sprintf("状态: %s\n", status)
|
||||
result += fmt.Sprintf("优先级: %s\n", t.Priority)
|
||||
result += fmt.Sprintf("创建时间: %s\n", t.CreatedAt.Format("2006-01-02 15:04:05"))
|
||||
result += fmt.Sprintf("更新时间: %s\n", t.UpdatedAt.Format("2006-01-02 15:04:05"))
|
||||
|
||||
if t.Completed && t.CompletedAt != nil {
|
||||
result += fmt.Sprintf("完成时间: %s\n", t.CompletedAt.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
if t.Description != "" {
|
||||
result += fmt.Sprintf("描述: %s\n", t.Description)
|
||||
}
|
||||
|
||||
if len(t.Tags) > 0 {
|
||||
result += fmt.Sprintf("标签: %v\n", t.Tags)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// IsOverdue 检查任务是否过期(如果有截止日期的话)
|
||||
func (t *Task) IsOverdue() bool {
|
||||
// 这里可以扩展添加截止日期功能
|
||||
return false
|
||||
}
|
||||
|
||||
// Age 返回任务的存在时间
|
||||
func (t *Task) Age() time.Duration {
|
||||
return time.Since(t.CreatedAt)
|
||||
}
|
||||
|
||||
// CompletionTime 返回任务的完成耗时
|
||||
func (t *Task) CompletionTime() time.Duration {
|
||||
if !t.Completed || t.CompletedAt == nil {
|
||||
return 0
|
||||
}
|
||||
return t.CompletedAt.Sub(t.CreatedAt)
|
||||
}
|
||||
|
||||
// Clone 创建任务的副本
|
||||
func (t *Task) Clone() *Task {
|
||||
clone := *t
|
||||
|
||||
// 深拷贝切片
|
||||
if len(t.Tags) > 0 {
|
||||
clone.Tags = make([]string, len(t.Tags))
|
||||
copy(clone.Tags, t.Tags)
|
||||
}
|
||||
|
||||
// 深拷贝时间指针
|
||||
if t.CompletedAt != nil {
|
||||
completedAt := *t.CompletedAt
|
||||
clone.CompletedAt = &completedAt
|
||||
}
|
||||
|
||||
return &clone
|
||||
}
|
||||
|
||||
// Validate 验证任务数据的有效性
|
||||
func (t *Task) Validate() error {
|
||||
if t.ID <= 0 {
|
||||
return fmt.Errorf("任务ID必须大于0")
|
||||
}
|
||||
|
||||
if t.Content == "" {
|
||||
return fmt.Errorf("任务内容不能为空")
|
||||
}
|
||||
|
||||
if t.Priority < Low || t.Priority > Urgent {
|
||||
return fmt.Errorf("无效的优先级")
|
||||
}
|
||||
|
||||
if t.CreatedAt.IsZero() {
|
||||
return fmt.Errorf("创建时间不能为空")
|
||||
}
|
||||
|
||||
if t.UpdatedAt.IsZero() {
|
||||
return fmt.Errorf("更新时间不能为空")
|
||||
}
|
||||
|
||||
if t.Completed && t.CompletedAt == nil {
|
||||
return fmt.Errorf("已完成的任务必须有完成时间")
|
||||
}
|
||||
|
||||
if !t.Completed && t.CompletedAt != nil {
|
||||
return fmt.Errorf("未完成的任务不应该有完成时间")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TaskList 任务列表类型
|
||||
type TaskList []Task
|
||||
|
||||
// Len 返回任务列表长度
|
||||
func (tl TaskList) Len() int {
|
||||
return len(tl)
|
||||
}
|
||||
|
||||
// Less 比较两个任务的顺序(用于排序)
|
||||
func (tl TaskList) Less(i, j int) bool {
|
||||
// 首先按完成状态排序(未完成的在前)
|
||||
if tl[i].Completed != tl[j].Completed {
|
||||
return !tl[i].Completed
|
||||
}
|
||||
|
||||
// 然后按优先级排序(高优先级在前)
|
||||
if tl[i].Priority != tl[j].Priority {
|
||||
return tl[i].Priority > tl[j].Priority
|
||||
}
|
||||
|
||||
// 最后按创建时间排序(新创建的在前)
|
||||
return tl[i].CreatedAt.After(tl[j].CreatedAt)
|
||||
}
|
||||
|
||||
// Swap 交换两个任务的位置
|
||||
func (tl TaskList) Swap(i, j int) {
|
||||
tl[i], tl[j] = tl[j], tl[i]
|
||||
}
|
||||
|
||||
// Filter 过滤任务列表
|
||||
func (tl TaskList) Filter(predicate func(Task) bool) TaskList {
|
||||
var filtered TaskList
|
||||
for _, task := range tl {
|
||||
if predicate(task) {
|
||||
filtered = append(filtered, task)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// FindByID 根据ID查找任务
|
||||
func (tl TaskList) FindByID(id int) (*Task, bool) {
|
||||
for i, task := range tl {
|
||||
if task.ID == id {
|
||||
return &tl[i], true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetCompleted 获取已完成的任务
|
||||
func (tl TaskList) GetCompleted() TaskList {
|
||||
return tl.Filter(func(t Task) bool {
|
||||
return t.Completed
|
||||
})
|
||||
}
|
||||
|
||||
// GetPending 获取待完成的任务
|
||||
func (tl TaskList) GetPending() TaskList {
|
||||
return tl.Filter(func(t Task) bool {
|
||||
return !t.Completed
|
||||
})
|
||||
}
|
||||
|
||||
// GetByPriority 根据优先级获取任务
|
||||
func (tl TaskList) GetByPriority(priority Priority) TaskList {
|
||||
return tl.Filter(func(t Task) bool {
|
||||
return t.Priority == priority
|
||||
})
|
||||
}
|
||||
|
||||
// Search 搜索包含关键词的任务
|
||||
func (tl TaskList) Search(keyword string) TaskList {
|
||||
return tl.Filter(func(t Task) bool {
|
||||
return contains(t.Content, keyword) ||
|
||||
contains(t.Description, keyword) ||
|
||||
containsInTags(t.Tags, keyword)
|
||||
})
|
||||
}
|
||||
|
||||
// contains 检查字符串是否包含子字符串(忽略大小写)
|
||||
func contains(s, substr string) bool {
|
||||
if substr == "" {
|
||||
return true
|
||||
}
|
||||
return len(s) >= len(substr) &&
|
||||
strings.Contains(strings.ToLower(s), strings.ToLower(substr))
|
||||
}
|
||||
|
||||
// containsInTags 检查标签列表是否包含关键词
|
||||
func containsInTags(tags []string, keyword string) bool {
|
||||
for _, tag := range tags {
|
||||
if contains(tag, keyword) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
Reference in New Issue
Block a user