feat: 新增语音转文字(ASR)功能

- 后端:新增 /api/audio/transcribe 接口,调用 Ollama whisper 进行语音识别
- 前端:新增 AudioTranscribePage.vue 页面,支持 MP3/WAV/M4A/OGG/FLAC 等格式
- 注册路由、工具卡片、智能助手欢迎语更新

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
eaiadmin
2026-09-14 00:53:36 +08:00
co-authored by Claude Code
parent 752c7837ef
commit 0455f064ac
299 changed files with 1588 additions and 432 deletions
@@ -0,0 +1,23 @@
package model
import "time"
// AiCallLog AI 调用日志(审计 + 按用户算力点计费)
// 精简自 pj034 ai_call_logs:去掉 company_id / input_asset / cost_cny / billing_mode 等电商与多租户字段。
type AiCallLog struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
Capability string `gorm:"size:32;not null;index" json:"capability"` // ai_chat / text_gen / embed
Provider string `gorm:"size:32" json:"provider"`
RouteID string `gorm:"size:64" json:"route_id"`
Model string `gorm:"size:64" json:"model"`
TokensInput int `gorm:"default:0" json:"tokens_input"`
TokensOutput int `gorm:"default:0" json:"tokens_output"`
CreditsCharged int `gorm:"default:0" json:"credits_charged"` // 实扣点数(0 = 未扣)
Status string `gorm:"size:16;not null;index" json:"status"` // success / failed
ErrorMessage string `gorm:"size:512" json:"error_message,omitempty"`
LatencyMs int `gorm:"default:0" json:"latency_ms"`
CreatedAt time.Time `json:"created_at"`
}
func (AiCallLog) TableName() string { return "ai_call_log" }
@@ -0,0 +1,20 @@
package model
import "time"
// Certificate 考试合格证书:正式考试通过后自动颁发,供培训验收与留存。
// 每张证书对应一条正式考试记录(同一学员同一试卷仅一次正式考,故不会重复发证)。
type Certificate struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
ExamRecordID uint `gorm:"not null;index" json:"exam_record_id"`
UserName string `gorm:"size:64;not null" json:"user_name"`
ExamName string `gorm:"size:128;not null" json:"exam_name"`
Score int `gorm:"not null" json:"score"`
TotalScore int `gorm:"not null" json:"total_score"`
PassScore int `gorm:"not null" json:"pass_score"`
CertNo string `gorm:"size:64;not null;uniqueIndex" json:"cert_no"`
IssuedAt time.Time `gorm:"not null" json:"issued_at"`
}
func (Certificate) TableName() string { return "certificate" }
@@ -0,0 +1,24 @@
package model
import "time"
// Course 课程表
type Course struct {
ID uint `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:32;uniqueIndex;not null" json:"code"`
Name string `gorm:"size:128;not null" json:"name"`
Category string `gorm:"size:32;not null;index" json:"category"` // capital_script/qualification_logic/ai_public_course/ai_platform_matching
TargetCustomers string `gorm:"type:text" json:"target_customers"`
ForbiddenCustomers string `gorm:"type:text" json:"forbidden_customers"`
Scripts string `gorm:"type:text" json:"scripts"`
SalesProcess string `gorm:"type:text" json:"sales_process"`
ObjectionHandling string `gorm:"type:text" json:"objection_handling"`
DeliveryPitfalls string `gorm:"type:text" json:"delivery_pitfalls"`
ReportRules string `gorm:"type:text" json:"report_rules"`
RelatedProductID *uint `gorm:"index" json:"related_product_id"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Course) TableName() string { return "course" }
@@ -0,0 +1,16 @@
package model
import "time"
// Department 部门字典表:企业组织架构,供用户「部门」字段选择与按部门学情统计。
// 用户以 user.department 字符串归属(历史兼容),部门改名时后端同步该字符串。
type Department struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:64;not null;uniqueIndex" json:"name"`
Description string `gorm:"size:255;default:''" json:"description"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Department) TableName() string { return "department" }
@@ -0,0 +1,22 @@
package model
import "time"
// ExamPaper 考试配置表
type ExamPaper struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128;not null" json:"name"`
Type string `gorm:"size:16;not null;index" json:"type"` // self_test / formal
Domain string `gorm:"size:64;not null" json:"domain"` // 抽题知识域,逗号分隔
QuestionCount int `gorm:"not null" json:"question_count"`
TotalScore int `gorm:"not null" json:"total_score"`
PassScore int `gorm:"not null" json:"pass_score"`
DurationMinutes int `gorm:"not null" json:"duration_minutes"`
Randomize bool `gorm:"not null" json:"randomize"`
PositionID *uint `gorm:"index" json:"position_id"` // 关联岗位考试,可空;设置后按岗位知识映射抽题
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (ExamPaper) TableName() string { return "exam_paper" }
@@ -0,0 +1,21 @@
package model
import "time"
// ExamRecord 考试记录表
type ExamRecord struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
PaperID uint `gorm:"not null;index" json:"paper_id"`
ExamName string `gorm:"size:128;not null" json:"exam_name"`
Score int `gorm:"not null" json:"score"`
TotalScore int `gorm:"not null" json:"total_score"`
PassScore int `gorm:"not null" json:"pass_score"`
Passed bool `gorm:"not null;index" json:"passed"`
CorrectCount int `gorm:"not null" json:"correct_count"`
WrongCount int `gorm:"not null" json:"wrong_count"`
DetailJSON string `gorm:"type:text;not null" json:"detail_json"` // 答题明细 JSON
SubmittedAt time.Time `gorm:"not null;index" json:"submitted_at"`
}
func (ExamRecord) TableName() string { return "exam_record" }
@@ -0,0 +1,18 @@
package model
import "time"
// KnowledgeChunk 知识块表(AI 检索最小单元)
type KnowledgeChunk struct {
ID uint `gorm:"primaryKey" json:"id"`
MediaFileID *uint `gorm:"index" json:"media_file_id"` // 与 knowledge_source_id 二选一
KnowledgeSourceID *uint `gorm:"index" json:"knowledge_source_id"` // 与 media_file_id 二选一
SourceType string `gorm:"size:32;not null" json:"source_type"` // pdf/doc/md
SourceID string `gorm:"size:64" json:"source_id"`
KnowledgeSpaceKey string `gorm:"size:64;index" json:"knowledge_space_key"`
ChunkIndex int `gorm:"not null" json:"chunk_index"`
Content string `gorm:"type:text;not null" json:"content"`
CreatedAt time.Time `json:"created_at"`
}
func (KnowledgeChunk) TableName() string { return "knowledge_chunk" }
@@ -0,0 +1,20 @@
package model
import "time"
// KnowledgeFAQ FAQ 标准问答表
type KnowledgeFAQ struct {
ID uint `gorm:"primaryKey" json:"id"`
KnowledgeSpaceKey string `gorm:"size:64;index" json:"knowledge_space_key"`
Question string `gorm:"size:512;not null" json:"question"`
Answer string `gorm:"type:text;not null" json:"answer"`
SimilarQuestions string `gorm:"type:text" json:"similar_questions"`
Keywords string `gorm:"size:512" json:"keywords"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
HitCount int `gorm:"not null;default:0" json:"hit_count"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (KnowledgeFAQ) TableName() string { return "knowledge_faq" }
@@ -0,0 +1,23 @@
package model
import "time"
// KnowledgeSource 结构化知识源表
type KnowledgeSource struct {
ID uint `gorm:"primaryKey" json:"id"`
Title string `gorm:"size:256;not null" json:"title"`
FilePath string `gorm:"size:512;uniqueIndex;not null" json:"file_path"`
Category string `gorm:"size:64;not null;index" json:"category"` // general/capital_consulting/qualification_counseling/ai_consulting/ai_tools_platform
Domain string `gorm:"size:16;not null;default:product" json:"domain"` // company / product / sales
SourceVersion string `gorm:"size:32;not null" json:"source_version"`
AuditStatus string `gorm:"size:16;not null;default:pending;index" json:"audit_status"` // pending / approved / rejected
KnowledgeSpaceKey string `gorm:"size:64;index" json:"knowledge_space_key"`
AuditBy *uint `json:"audit_by"`
AuditAt *time.Time `json:"audit_at"`
RejectReason string `gorm:"size:512" json:"reject_reason"`
Ingested bool `gorm:"not null;default:false" json:"ingested"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (KnowledgeSource) TableName() string { return "knowledge_source" }
@@ -0,0 +1,19 @@
package model
import "time"
// KnowledgeSpace 知识空间定义,供知识库应用与知识库管理共享。
type KnowledgeSpace struct {
ID uint `gorm:"primaryKey" json:"id"`
Key string `gorm:"size:64;uniqueIndex;not null" json:"key"`
Name string `gorm:"size:128;not null" json:"name"`
Description string `gorm:"size:512" json:"description"`
Scope string `gorm:"size:32;not null;default:general" json:"scope"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"`
SortOrder int `gorm:"not null;default:0" json:"sort_order"`
IsDefault bool `gorm:"not null;default:false" json:"is_default"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (KnowledgeSpace) TableName() string { return "knowledge_space" }
@@ -0,0 +1,15 @@
package model
import "time"
// LearningProgress 学习进度表:记录员工是否已浏览公司介绍 / 产品 / 课程
type LearningProgress struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"uniqueIndex:idx_learning;not null" json:"user_id"`
ItemType string `gorm:"uniqueIndex:idx_learning;size:16;not null" json:"item_type"` // company / product / course
ItemID uint `gorm:"uniqueIndex:idx_learning;not null" json:"item_id"` // company 固定 0
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (LearningProgress) TableName() string { return "learning_progress" }
@@ -0,0 +1,27 @@
package model
import "time"
// MediaFile 素材文件表
type MediaFile struct {
ID uint `gorm:"primaryKey" json:"id"`
Filename string `gorm:"size:256;not null" json:"filename"`
StoredName string `gorm:"size:64;not null" json:"stored_name"`
StoredPath string `gorm:"size:512;not null" json:"stored_path"`
FileExt string `gorm:"size:16;not null" json:"file_ext"`
FileSize int64 `gorm:"not null" json:"file_size"`
Status string `gorm:"size:16;not null;default:pending;index" json:"status"` // pending / approved / rejected
Source string `gorm:"size:16;not null" json:"source"` // employee / admin
SubmitterID uint `gorm:"not null;index" json:"submitter_id"`
BindType string `gorm:"size:16;not null;default:none" json:"bind_type"` // company / product / course / none
BindID *uint `gorm:"index" json:"bind_id"`
Remark string `gorm:"size:512" json:"remark"` // 提交备注(员工素材建议)
RejectReason string `gorm:"size:512" json:"reject_reason"`
AuditBy *uint `json:"audit_by"`
AuditAt *time.Time `json:"audit_at"`
KnowledgeSpaceKey string `gorm:"size:64;index" json:"knowledge_space_key"`
Extracted bool `gorm:"not null;default:false;index" json:"extracted"`
CreatedAt time.Time `json:"created_at"`
}
func (MediaFile) TableName() string { return "media_file" }
@@ -0,0 +1,23 @@
package model
import "time"
// MistakeRecord 错题本(学员自助工具,非学情分析)
// 按 (user_id, question_id, source) 去重:同一学员同一题同一种考试只保留一条,
// 再次答错时更新作答与解析并将 resolved 重置为 false。
type MistakeRecord struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
QuestionID uint `gorm:"not null;index" json:"question_id"`
Source string `gorm:"size:16;not null;index" json:"source"` // self_test / formal
QuestionType string `gorm:"size:16;not null" json:"question_type"` // single/multiple/judge/essay(冗余,供错题本独立展示)
QuestionStem string `gorm:"type:text;not null" json:"question_stem"`
UserAnswer string `gorm:"type:text" json:"user_answer"` // JSON 序列化的作答
CorrectAnswer string `gorm:"type:text" json:"correct_answer"` // JSON 序列化的正确答案 / 评分标准
Explanation string `gorm:"type:text;default:''" json:"explanation"`
Resolved bool `gorm:"not null;default:false;index" json:"resolved"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (MistakeRecord) TableName() string { return "mistake_record" }
@@ -0,0 +1,17 @@
package model
import "time"
// Notification 站内消息通知:考试发布 / 考试通过发证 / 岗位设置等事件推送给学员。
type Notification struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
Type string `gorm:"size:32;not null;index" json:"type"` // exam_publish / exam_pass / position_set
Title string `gorm:"size:128;not null" json:"title"`
Content string `gorm:"type:text;not null" json:"content"`
Link string `gorm:"size:128;default:''" json:"link"` // 前端跳转路由
Read bool `gorm:"not null;default:false;index" json:"read"`
CreatedAt time.Time `json:"created_at"`
}
func (Notification) TableName() string { return "notification" }
@@ -0,0 +1,40 @@
package model
import "time"
// OfficialAccountArticle 公众号文章状态表,对齐工作流中的主题/标题/提纲/正文/配图/预览/分页显式传递。
type OfficialAccountArticle struct {
ID uint `gorm:"primaryKey" json:"id"`
TaskID uint `gorm:"not null;uniqueIndex" json:"task_id"`
SpecialistKey string `gorm:"size:64;not null;index" json:"specialist_key"`
BusinessDomain string `gorm:"size:32;not null;default:ai;index" json:"business_domain"`
Keyword string `gorm:"size:256;not null" json:"keyword"`
Audience string `gorm:"size:128;not null" json:"audience"`
Goal string `gorm:"size:128;not null" json:"goal"`
Tone string `gorm:"size:128;not null" json:"tone"`
Requirements string `gorm:"type:text" json:"requirements"`
TopicCandidatesJSON string `gorm:"type:text" json:"topic_candidates_json"`
HotspotsJSON string `gorm:"type:text" json:"hotspots_json"`
SelectedTopic string `gorm:"size:256" json:"selected_topic"`
TopicHeat string `gorm:"size:64" json:"topic_heat"`
TitleCandidatesJSON string `gorm:"type:text" json:"title_candidates_json"`
SelectedTitle string `gorm:"size:256" json:"selected_title"`
OutlineStyle string `gorm:"size:64;not null;default:问题拆解型" json:"outline_style"`
OutlineWords int `gorm:"not null;default:600" json:"outline_words"`
Outline string `gorm:"type:text" json:"outline"`
ContentTargetWords int `gorm:"not null;default:1400" json:"content_target_words"`
MaxContentImages int `gorm:"not null;default:0" json:"max_content_images"`
Content string `gorm:"type:text" json:"content"`
ContentWithPrompts string `gorm:"type:text" json:"content_with_prompts"`
ImagePromptsJSON string `gorm:"type:text" json:"image_prompts_json"`
ImageRefsJSON string `gorm:"type:text" json:"image_refs_json"`
PreviewMarkdown string `gorm:"type:text" json:"preview_markdown"`
PreviewHTML string `gorm:"type:text" json:"preview_html"`
PageMarkdown string `gorm:"type:text" json:"page_markdown"`
PagesJSON string `gorm:"type:text" json:"pages_json"`
Status string `gorm:"size:32;not null;default:draft;index" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (OfficialAccountArticle) TableName() string { return "official_account_article" }
@@ -0,0 +1,25 @@
package model
import "time"
// OfficialAccountHotspot 业务域热点缓存表,承接 RSS/网页抓取结果及领域评分。
type OfficialAccountHotspot struct {
ID uint `gorm:"primaryKey" json:"id"`
BusinessDomain string `gorm:"size:32;not null;default:ai;index" json:"business_domain"`
SourceKey string `gorm:"size:64;not null;index;uniqueIndex:idx_hotspot_source_url" json:"source_key"`
SourceLabel string `gorm:"size:128;not null" json:"source_label"`
SourceType string `gorm:"size:16;not null" json:"source_type"`
Title string `gorm:"size:512;not null" json:"title"`
URL string `gorm:"size:1024;not null;uniqueIndex:idx_hotspot_source_url" json:"url"`
Summary string `gorm:"type:text" json:"summary"`
PublishedAt *time.Time `json:"published_at"`
RawScore float64 `gorm:"not null;default:0" json:"raw_score"`
DomainScore float64 `gorm:"not null;default:0" json:"domain_score"`
HeatLabel string `gorm:"size:32;default:''" json:"heat_label"`
TagsJSON string `gorm:"type:text" json:"tags_json"`
FetchedAt time.Time `gorm:"index" json:"fetched_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (OfficialAccountHotspot) TableName() string { return "official_account_hotspot" }
@@ -0,0 +1,17 @@
package model
import "time"
// PointEvent 学习积分流水(游戏化成长值,逐笔可审计)
// 用户学习/考试/错题掌握等行为触发加分,积分总额冗余在 user.learning_points。
type PointEvent struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
EventType string `gorm:"size:32;not null;index" json:"event_type"` // first_company/first_product/first_course/self_test/formal_pass/mistake_resolved
Points int `gorm:"not null" json:"points"`
RefType string `gorm:"size:32;default:''" json:"ref_type"` // company/product/course/paper/mistake
RefID uint `gorm:"default:0" json:"ref_id"`
CreatedAt time.Time `json:"created_at"`
}
func (PointEvent) TableName() string { return "point_event" }
@@ -0,0 +1,27 @@
package model
import "time"
// 岗位知识要求级别(对齐 pj006 的 L1-L4 词汇)
const (
LevelL1 = "L1" // 入门级:概念/定义/标准流程
LevelL2 = "L2" // 稳定执行级:标准应用/独立执行
LevelL3 = "L3" // 复杂处理级:跨场景迁移/带教
LevelL4 = "L4" // 体系策略级:方案设计/组织约束
)
// ValidLevels 合法级别集合
var ValidLevels = map[string]bool{LevelL1: true, LevelL2: true, LevelL3: true, LevelL4: true}
// Position 岗位表
type Position struct {
ID uint `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:32;uniqueIndex;not null" json:"code"`
Name string `gorm:"size:64;not null" json:"name"`
Description string `gorm:"type:text" json:"description"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Position) TableName() string { return "position" }
@@ -0,0 +1,18 @@
package model
import "time"
// PositionExamBlueprint 岗位考试蓝图:规定岗位考试的题型构成(按域+题型各抽多少题)。
// 题目本身不携带 L1-L4 级别字段,故蓝图按 domain + type 维度抽样,不做级别加权。
// 蓝图存在时,pickQuestions 优先按蓝图组卷;否则回退到岗位知识映射圈定题池抽题。
type PositionExamBlueprint struct {
ID uint `gorm:"primaryKey" json:"id"`
PositionID uint `gorm:"not null;index" json:"position_id"`
Domain string `gorm:"size:16;not null;default:''" json:"domain"` // company/product/sales,空=不限(在岗位范围内)
Type string `gorm:"size:16;not null" json:"type"` // single/multiple/judge/essay
Count int `gorm:"not null" json:"count"` // 该域该题型抽取数量
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PositionExamBlueprint) TableName() string { return "position_exam_blueprint" }
@@ -0,0 +1,20 @@
package model
import "time"
// PositionKnowledge 岗位知识要求(岗位 ↔ 知识域/课程/产品 映射)
// domain 必填;course_id / product_id 可选(三者共同圈定岗位应学范围)。
type PositionKnowledge struct {
ID uint `gorm:"primaryKey" json:"id"`
PositionID uint `gorm:"not null;index" json:"position_id"`
Domain string `gorm:"size:16;not null;index" json:"domain"` // company / product / sales
CourseID *uint `gorm:"index" json:"course_id"` // 绑定具体课程,可空
ProductID *uint `gorm:"index" json:"product_id"` // 绑定具体产品,可空
RequiredLevel string `gorm:"size:16;not null;default:L1" json:"required_level"` // L1/L2/L3/L4
Weight float64 `gorm:"not null;default:1" json:"weight"`
IsMandatory bool `gorm:"not null;default:true" json:"is_mandatory"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (PositionKnowledge) TableName() string { return "position_knowledge" }
@@ -0,0 +1,24 @@
package model
import "time"
// Product 产品表
type Product struct {
ID uint `gorm:"primaryKey" json:"id"`
Code string `gorm:"size:32;uniqueIndex;not null" json:"code"`
Name string `gorm:"size:128;not null" json:"name"`
Category string `gorm:"size:32;not null;index" json:"category"` // capital_consulting/qualification_counseling/ai_consulting/ai_tools_platform
Tags string `gorm:"size:256;default:''" json:"tags"`
Description string `gorm:"type:text" json:"description"`
Pricing string `gorm:"type:text" json:"pricing"`
CommissionRecommend string `gorm:"size:128;default:''" json:"commission_recommend"`
CommissionNegotiate string `gorm:"size:128;default:''" json:"commission_negotiate"`
PublicCourseBonus string `gorm:"size:128;default:''" json:"public_course_bonus"`
VersionRisk string `gorm:"type:text" json:"version_risk"`
ReportRules string `gorm:"type:text" json:"report_rules"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Product) TableName() string { return "product" }
@@ -0,0 +1,20 @@
package model
import "time"
// Question 题目表
type Question struct {
ID uint `gorm:"primaryKey" json:"id"`
Domain string `gorm:"size:16;not null;index" json:"domain"` // company / product / sales
CourseID *uint `gorm:"index" json:"course_id"`
Type string `gorm:"size:16;not null" json:"type"` // single / multiple / judge
Stem string `gorm:"type:text;not null" json:"stem"`
Options string `gorm:"type:text;not null" json:"options"` // JSON 数组:["A","B","C"]
Answer string `gorm:"type:text;not null" json:"answer"` // JSON 数组:single=[索引] multiple=[索引...] judge=[bool]
Explanation string `gorm:"type:text;default:''" json:"explanation"`
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / inactive
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Question) TableName() string { return "question" }
@@ -0,0 +1,38 @@
package model
import "time"
// Specialist 数字员工专员目录
type Specialist struct {
ID uint `gorm:"primaryKey" json:"id"`
Key string `gorm:"size:64;uniqueIndex;not null" json:"key"`
Label string `gorm:"size:128;not null" json:"label"`
Tier string `gorm:"size:16;not null;index" json:"tier"` // generic / industry
WorkerType string `gorm:"size:16;not null;default:dw;index" json:"worker_type"`
Route string `gorm:"size:128;not null" json:"route"`
Summary string `gorm:"type:text" json:"summary"`
WorkStatus string `gorm:"size:64;default:''" json:"work_status"`
RiskLabel string `gorm:"size:64;default:''" json:"risk_label"`
Color string `gorm:"size:16;default:''" json:"color"`
Stage string `gorm:"size:64;default:''" json:"stage"`
Progress int `gorm:"not null;default:0" json:"progress"`
MarketTag string `gorm:"size:32;not null;default:installed;index" json:"market_tag"` // 已安装 / 可升级 / 试用
Version string `gorm:"size:32;default:''" json:"version"`
ConnectorScope string `gorm:"type:text" json:"connector_scope"`
PermissionScope string `gorm:"type:text" json:"permission_scope"`
ResourceBindings string `gorm:"type:text" json:"resource_bindings"`
InfoSources string `gorm:"type:text" json:"info_sources"`
BaseSkills string `gorm:"type:text" json:"base_skills"`
AIAssistance string `gorm:"type:text" json:"ai_assistance"`
GeneratedSkills string `gorm:"type:text" json:"generated_skills"`
InputsRecordsJSON string `gorm:"column:source_records_json;type:text" json:"inputs_records_json"`
PermissionRecordsJSON string `gorm:"type:text" json:"permission_records_json"`
ActionRecordsJSON string `gorm:"type:text" json:"action_records_json"`
ResultRecordsJSON string `gorm:"type:text" json:"result_records_json"`
State string `gorm:"size:16;not null;default:active;index" json:"state"` // active / inactive
SortOrder int `gorm:"not null;default:0;index" json:"sort_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (Specialist) TableName() string { return "specialist" }
@@ -0,0 +1,16 @@
package model
import "time"
// StudyNote 学习笔记:员工在浏览公司介绍 / 产品 / 课程时记录的私人学习笔记。
type StudyNote struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"not null;index" json:"user_id"`
ItemType string `gorm:"size:16;not null;index" json:"item_type"` // company / product / course
ItemID uint `gorm:"not null;index" json:"item_id"` // company 固定 0
Content string `gorm:"type:text;not null" json:"content"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (StudyNote) TableName() string { return "study_note" }
@@ -0,0 +1,14 @@
package model
import "time"
// SystemConfig 系统参数配置表
type SystemConfig struct {
ID uint `gorm:"primaryKey" json:"id"`
ConfigKey string `gorm:"size:64;uniqueIndex;not null" json:"config_key"`
ConfigValue string `gorm:"type:text;not null" json:"config_value"`
Description string `gorm:"size:256;default:''" json:"description"`
UpdatedAt time.Time `json:"updated_at"`
}
func (SystemConfig) TableName() string { return "system_config" }
@@ -0,0 +1,23 @@
package model
import "time"
// User 用户表
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"size:64;uniqueIndex;not null" json:"username"`
PasswordHash string `gorm:"size:256;not null" json:"-"`
FullName string `gorm:"size:64;not null" json:"full_name"`
Role string `gorm:"size:16;not null;default:employee;index" json:"role"` // employee / admin
Status string `gorm:"size:16;not null;default:active;index" json:"status"` // active / disabled
AiPoints int `gorm:"not null;default:100" json:"ai_points"` // 剩余 AI 算力点
LearningPoints int `gorm:"not null;default:0;index" json:"learning_points"` // 学习积分(游戏化成长值)
Department string `gorm:"size:64;default:''" json:"department"` // 部门
Position string `gorm:"size:64;default:''" json:"position"` // 岗位(文本,历史遗留)
PositionID *uint `gorm:"index" json:"position_id"` // 所属岗位,可空(历史用户/管理员可为空)
HireBatch string `gorm:"size:64;default:''" json:"hire_batch"` // 入职批次
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (User) TableName() string { return "user" }
@@ -0,0 +1,21 @@
package model
import "time"
// WorkerArtifact 专员交付物
type WorkerArtifact struct {
ID uint `gorm:"primaryKey" json:"id"`
TaskID uint `gorm:"not null;index" json:"task_id"`
SpecialistKey string `gorm:"size:64;not null;index" json:"specialist_key"`
Title string `gorm:"size:128;not null" json:"title"`
ArtifactType string `gorm:"size:32;not null;default:text" json:"artifact_type"`
Status string `gorm:"size:32;not null;default:draft;index" json:"status"`
ContentText string `gorm:"type:text" json:"content_text"`
ContentJSON string `gorm:"type:text" json:"content_json"`
SourceRefsJSON string `gorm:"type:text" json:"source_refs_json"`
CreatedByRunID *uint `gorm:"index" json:"created_by_run_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (WorkerArtifact) TableName() string { return "worker_artifact" }
@@ -0,0 +1,23 @@
package model
import "time"
// WorkerRun 专员动作执行记录
type WorkerRun struct {
ID uint `gorm:"primaryKey" json:"id"`
TaskID uint `gorm:"not null;index" json:"task_id"`
SpecialistKey string `gorm:"size:64;not null;index" json:"specialist_key"`
ActionKey string `gorm:"size:64;not null;index" json:"action_key"`
ActionTitle string `gorm:"size:128;not null" json:"action_title"`
ActionType string `gorm:"size:32;default:''" json:"action_type"`
Status string `gorm:"size:16;not null;default:done;index" json:"status"`
InputJSON string `gorm:"type:text" json:"input_json"`
OutputJSON string `gorm:"type:text" json:"output_json"`
LogsJSON string `gorm:"type:text" json:"logs_json"`
StartedAt time.Time `json:"started_at"`
FinishedAt *time.Time `json:"finished_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (WorkerRun) TableName() string { return "worker_run" }
@@ -0,0 +1,24 @@
package model
import "time"
// WorkerTask 专员事项/任务
type WorkerTask struct {
ID uint `gorm:"primaryKey" json:"id"`
SpecialistKey string `gorm:"size:64;not null;index" json:"specialist_key"`
Title string `gorm:"size:128;not null" json:"title"`
Summary string `gorm:"type:text" json:"summary"`
Owner string `gorm:"size:64;default:''" json:"owner"`
Priority string `gorm:"size:16;not null;default:P2" json:"priority"`
Status string `gorm:"size:32;not null;default:待处理;index" json:"status"`
ContextJSON string `gorm:"type:text" json:"context_json"`
CurrentRunID *uint `gorm:"index" json:"current_run_id"`
CurrentResult string `gorm:"type:text" json:"current_result"`
DueAt *time.Time `json:"due_at"`
CreatedBy *uint `gorm:"index" json:"created_by"`
LastTriggeredAt *time.Time `json:"last_triggered_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (WorkerTask) TableName() string { return "worker_task" }