30 KiB
知识库初始版(蚕病百科 + AI 结果解读)Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 交付知识库初始版:Go 后端蚕病百科/知识文章 API + 规格书种子数据,Web 后台管理页,小程序农户查看页。
Architecture: 沿用现有分层(Go/Gin/GORM、React/AntD Pro、Taro 4)。新增 diseases(蚕病百科)与 knowledge_articles(AI 结果解读等文章)两表;CRUD 复用现有 handler 模式与 bindUpdates;种子数据启动时幂等写入;权限码 knowledge:read / knowledge:write 控制读写。
Tech Stack: Go 1.23 / Gin / GORM(PostgreSQL);React 19 / antd 5 / @ant-design/pro-components;Taro 4 + React + Sass。
Global Constraints
- 不得修改生成产物(
miniapp/dist/、web/dist/);只改源文件。 - 蚕病内容以规格书
.cc-connect/attachments/蚕病智能防控平台规格说明书.md附录 11.1、3.1.4(风险分级)、3.8(处置方向/排查清单)为准,不编造。 - 风险分级阈值(绿 0-30 / 黄 31-60 / 橙 61-80 / 红 81-100)来自规格书 3.1.4。
- 权限:读
knowledge:read,写knowledge:write。 - 字段风格沿用现有约定:Go JSON camelCase、DB 列 snake_case、web/miniapp 接口类型与后端 JSON 一致。
- 验证命令:后端
go test ./... && go build ./... && go vet ./...;Webnpm run lint && npm run build;小程序npm run build:weapp。 - 本仓库当前无前端测试基建(Vitest/Jest 均未配置,属后续工作计划 #27),UI 验证以 lint + 类型检查 + 构建为准;Go 侧对纯逻辑(种子数据)写单元测试(TDD)。
Task 1: Go 模型 + 种子数据测试(TDD RED)
Files:
- Create:
server-go/internal/model/knowledge.go - Test:
server-go/internal/model/knowledge_seed_test.go
Interfaces:
-
Produces:
model.Disease、model.KnowledgeArticle结构体;包变量model.SeedDiseases []Disease、model.SeedKnowledgeArticles []KnowledgeArticle(Task 2 填充内容)。 -
Step 1: 写失败测试(先建
knowledge.go只含结构体定义,不含种子变量)
server-go/internal/model/knowledge_seed_test.go:
package model
import "testing"
func TestSeedDiseasesCoverCoreFour(t *testing.T) {
core := []string{"核型多角体病", "白僵病", "软化病", "微粒子病"}
got := map[string]bool{}
for _, d := range SeedDiseases {
got[d.Name] = true
}
for _, name := range core {
if !got[name] {
t.Errorf("核心蚕病缺失: %s", name)
}
}
}
func TestSeedDiseasesRequiredFields(t *testing.T) {
for _, d := range SeedDiseases {
if d.Name == "" || d.Category == "" {
t.Errorf("病种名称/类别不能为空: %+v", d)
}
if d.Pathogen == "" || d.Symptoms == "" || d.Transmission == "" || d.Prevention == "" {
t.Errorf("病种必备字段缺失(病原/症状/传播途径/防治指南): %s", d.Name)
}
}
}
func TestSeedDiseasesNamesUnique(t *testing.T) {
seen := map[string]bool{}
for _, d := range SeedDiseases {
if seen[d.Name] {
t.Errorf("病种名称重复: %s", d.Name)
}
seen[d.Name] = true
}
}
func TestSeedKnowledgeArticlesHasAIGuide(t *testing.T) {
found := false
for _, a := range SeedKnowledgeArticles {
if a.Kind == "ai_guide" && a.Title != "" && a.Content != "" {
found = true
}
}
if !found {
t.Error("缺少 AI 结果解读文章(kind=ai_guide)")
}
}
- Step 2: 运行测试确认失败
Run: cd server-go && go test ./internal/model/ -run TestSeed -v
Expected: 编译失败 undefined: SeedDiseases / undefined: SeedKnowledgeArticles(或测试失败)
-
Step 3: 最小实现:
knowledge.go定义结构体(见下方 Task 2 代码中结构体部分),并声明空变量var SeedDiseases []Disease、var SeedKnowledgeArticles []KnowledgeArticle使编译通过、测试失败。 -
Step 4: 再运行测试,确认失败原因是数据缺失(如
核心蚕病缺失: 核型多角体病),而非编译错误。 -
Step 5: Commit
git add server-go/internal/model/knowledge.go server-go/internal/model/knowledge_seed_test.go
git commit -m "test(knowledge): seed 数据完整性测试(RED)"
Task 2: Go 种子数据填充(TDD GREEN)
Files:
- Create:
server-go/internal/model/knowledge_seed.go(完整内容见下方代码块)
Interfaces:
-
Consumes: Task 1 的
Disease/KnowledgeArticle结构体。 -
Produces:
SeedDiseases(9 条,含 4 类核心病种)、SeedKnowledgeArticles(1 条 AI 结果解读)。字段值全部来自规格书附录 11.1 与 3.1.4;Prevention(防治指南)按规格书 3.8 处置方向(内源扩散/外源侵入)与分病种排查清单整理。 -
Step 1: 写入种子数据(代码见下)
package model
// SeedDiseases 蚕病百科种子数据(内容来源:规格书附录 11.1 / 3.8)
var SeedDiseases = []Disease{
{
Name: "核型多角体病", Category: "viral", SortOrder: 1,
Pathogen: "家蚕核型多角体病毒(杆状病毒科,双链DNA病毒)",
Transmission: "食下传染为主,创伤传染次之",
Medium: "多角体(2-6μm包涵体,存在于病蚕血液、脓汁、粪便中,污染蚕座、桑叶、蚕具)",
Incubation: "3-6天",
Symptoms: "体色乳白、环节肿胀如竹节、体壁紧张发亮、狂躁爬行、体壁易破流乳白色脓汁",
HighRiskStage: "5龄后期至蛹期,春蚕期较多发",
HighRiskCondition: "温度突变、密集饲养等环境应激可诱发潜伏感染转急性",
LethalTime: "感染后3-5天",
SpreadTrend: "病蚕体壁破裂流脓→脓汁污染蚕座桑叶→健康蚕食下感染→群体内快速扩散",
Recurrence: "多角体环境存活2-3年,不彻底消毒则跨批次跨年度反复发病",
Detection: "LAMP 靶标:polh 基因(已有引物序列)",
Prevention: "立即隔离病蚕并深埋;对发病区域及周边彻底消毒;上批次发病后升级消毒方案(含蚕具浸泡);排查蚕具跨批次共用、桑叶来源变更、人员进出记录;保持温湿度稳定、控制饲养密度",
},
{
Name: "白僵病", Category: "fungal", SortOrder: 2,
Pathogen: "白僵菌(丛梗孢科,兼性寄生菌——既有寄生能力又能腐生)",
Transmission: "接触传染为主(分生孢子接触蚕体壁→发芽侵入→体腔内繁殖),创伤传染次之,一般不能食下传染",
Medium: "分生孢子,通过气流扩散,是真菌病传播的核心媒介",
Incubation: "2-6天",
Symptoms: "体色暗淡、油渍斑、行动呆滞、死蚕僵硬覆白粉",
HighRiskStage: "温度25-30℃、湿度>75%时孢子萌发,连续阴雨高发",
HighRiskCondition: "湿度>75%是孢子萌发关键条件,连续阴雨",
LethalTime: "小蚕2-3天,大蚕5-6天",
SpreadTrend: "点发→面发→暴发;尸体持续产孢,适宜湿度下指数级扩散",
Recurrence: "白僵菌广泛寄生于桑园害虫和野外昆虫,可腐生存活,极难彻底清除",
Detection: "早期无症状识别难,需结合湿度监测与 AI 巡检",
Prevention: "立即隔离病蚕并深埋,防止尸体产孢扩散;湿度>75%时加强通风排湿;检查并加装通风口过滤网;加强人员/蚕具进出管控;蚕房周边昆虫防治、桑园害虫治理",
},
{
Name: "软化病", Category: "bacterial", SortOrder: 3,
Pathogen: "肠道致病菌(多种细菌)",
Transmission: "食下传染,环境恶化引发肠道菌群失调,与饲养管理密切相关",
Medium: "污染桑叶、蚕座环境",
Incubation: "病程缓慢,5龄集中暴发",
Symptoms: "食桑减少、排念珠状粪、胸部半透明(空头蚕)、群体发育不齐",
HighRiskStage: "蚕头密度大、通风不良、桑叶潮湿,5龄期集中暴发",
HighRiskCondition: "密度过大、通风不良、桑叶潮湿",
SpreadTrend: "往往群体性发生,同批次扩散;常存在混合感染",
Recurrence: "与饲养管理密切相关,密度/通风/桑叶卫生不改善易反复",
Detection: "与质型多角体病肉眼难以区分,需分子检测(16S rRNA / LAMP)确认",
Prevention: "控制蚕头密度、加强通风、避免桑叶潮湿;发现病蚕及时隔离;桑叶来源批次追溯,排查桑园害虫粪便污染;混合感染风险高,建议结合检测确认病原",
},
{
Name: "微粒子病", Category: "protozoan", SortOrder: 4,
Pathogen: "家蚕微粒子虫(Nosema bombycis),细胞内专性寄生的微孢子虫",
Transmission: "双重传播:食下传染(水平传播)+ 胚种传染(垂直传播,经卵传给下一代)",
Medium: "孢子,通过粪便、蚕蜕、尸体污染环境;通过卵细胞侵入子代",
Incubation: "慢性,可整个幼虫期无明显外观异常",
Symptoms: "发育不齐、群体陆续死亡、蚕体瘦小",
HighRiskStage: "种蚕生产中发病率可达50-60%;跨代累积,越种越弱",
HighRiskCondition: "蚕种来源不明、消毒不彻底",
SpreadTrend: "水平传播(病蚕排孢污染环境)+ 垂直传播(带毒母蛾产卵→子代先天感染→跨代累积)",
Recurrence: "唯一法定检疫对象;胚种传染使带毒批次代代相传",
Detection: "法定检疫:母蛾镜检;qPCR 已开展推广培训;LAMP 可参考文献设计引物",
Prevention: "种场必须逐批检测,蚕种来源全链追溯(母蛾镜检→蚕种批次→种场供种链);隔离发病批次;可喷施“防微灵”(国内唯一获兽药批文的治疗药物)后饲蚕;排查桑园周边野外昆虫交叉感染",
},
{
Name: "质型多角体病", Category: "viral", SortOrder: 5,
Pathogen: "家蚕质型多角体病毒(呼肠孤病毒科,双链RNA病毒)",
Transmission: "食下传染(经口),侵入中肠上皮细胞",
Medium: "多角体随病蚕粪便排出,持续污染蚕座和桑叶",
Incubation: "病程较缓慢",
Symptoms: "发育缓慢、体躯瘦小、呆滞、体色略带乳白、排出带乳白色的软粪",
HighRiskStage: "夏秋蚕期多发(与核型多角体病的春蚕期形成对比)",
HighRiskCondition: "夏秋蚕期高温闷热",
SpreadTrend: "病蚕排毒(粪便含病毒)→蚕座环境污染→同群体经口反复感染;病程缓慢但粪便持续污染使群体发病率累积上升;可通过卵传给下一代",
Recurrence: "可通过卵传代,在种群中形成病毒流行病",
Detection: "症状与软化病肉眼难以区分,需分子检测确认",
Prevention: "及时清除病蚕与蚕座污染物,防止粪便持续排毒;夏秋高温闷热期加强通风降温;同区域其他蚕房发病关联排查",
},
{
Name: "浓核病/传染性软化病", Category: "viral", SortOrder: 6,
Pathogen: "浓核病毒(BmDNV)、二分浓核病毒(BmBDV)、传染性软化病毒(BmIFV)",
Transmission: "食下感染",
Medium: "传染源主要来自桑园害虫粪便污染桑叶",
Incubation: "—",
Symptoms: "空头、下痢、吐液(与质型多角体病肉眼难以区分)",
HighRiskStage: "夏秋蚕期多发",
HighRiskCondition: "桑叶被桑园害虫粪便污染",
SpreadTrend: "桑园害虫粪便污染桑叶→食下感染→慢性消耗死亡或继发细菌感染;常存在混合感染",
Recurrence: "病原来自桑园害虫粪便污染,桑园防治不到位易反复",
Detection: "裸露病毒,不形成包涵体(多角体),普通显微镜无法观察,诊断难度大",
Prevention: "桑叶来源批次追溯(哪个桑园、哪天采摘);核查桑园近期害虫防治记录;保证桑叶储运条件;识别混合感染(常与质型多角体病混合)",
},
{
Name: "细菌性败血病", Category: "bacterial", SortOrder: 7,
Pathogen: "苏云金杆菌等多种细菌",
Transmission: "创伤传染——细菌通过蚕体表伤口侵入血腔",
Medium: "环境中广泛存在的细菌,通过蚕具刮伤、蚕互相抓伤等创口侵入",
Incubation: "—",
Symptoms: "密集饲养下蚕体互相抓伤概率增大→个体发病死亡→尸体腐烂细菌扩散",
HighRiskStage: "密集饲养、蚕具粗糙时多发",
HighRiskCondition: "蚕具刮伤、操作粗暴、消毒不彻底",
SpreadTrend: "个体发病死亡→尸体腐烂细菌扩散→接触传染加重",
Recurrence: "反复发作时需排查蚕具毛刺/操作粗暴/消毒盲区",
Detection: "—",
Prevention: "排查蚕具是否有尖锐边缘/毛刺;控制蚕头密度减少相互抓伤;给桑操作轻柔;反复发作时排查消毒盲区并升级消毒",
},
{
Name: "猝倒病", Category: "bacterial", SortOrder: 8,
Pathogen: "细菌毒素(如苏云金杆菌毒素)",
Transmission: "食下传染——蚕食入细菌产生的毒素",
Medium: "桑叶被细菌污染",
Incubation: "急性中毒,发病快、死亡快",
Symptoms: "急性中毒,发病快、死亡快",
HighRiskStage: "—",
HighRiskCondition: "桑叶储运环节污染",
SpreadTrend: "与桑叶污染直接相关",
Recurrence: "桑叶储运环节污染或蚕房环境",
Detection: "—",
Prevention: "桑叶来源批次追溯并检查储运条件;发现后立即更换桑叶来源;避免桑叶被细菌污染",
},
{
Name: "其他真菌病(绿僵病/黄僵病/曲霉病)", Category: "fungal", SortOrder: 9,
Pathogen: "金龟子绿僵菌(绿僵病)、曲霉菌(曲霉病)、黄僵病病原(较少见)",
Transmission: "接触传染、气流扩散(真菌分生孢子)",
Medium: "分生孢子",
Incubation: "—",
Symptoms: "绿僵病死蚕呈绿色;黄僵病较少见;曲霉病多发于小蚕期",
HighRiskStage: "曲霉病多发于小蚕期;高温高湿季真菌病暴发",
HighRiskCondition: "高温高湿、蚕座卫生差",
SpreadTrend: "绿僵病多为野外昆虫交叉感染",
Recurrence: "真菌孢子兼性腐生,环境残留难彻底清除",
Detection: "按死蚕孢子颜色肉眼初步区分",
Prevention: "与白僵病防控一致:加强通风排湿(湿度>75%重点防范);小蚕期注意蚕座卫生防曲霉病;蚕房周边昆虫防治与桑园害虫治理",
},
}
// SeedKnowledgeArticles 知识文章种子数据(AI 结果解读,分级阈值来自规格书 3.1.4)
var SeedKnowledgeArticles = []KnowledgeArticle{
{
Kind: "ai_guide",
Title: "AI 巡检结果怎么看(风险分级解读)",
Summary: "了解 AI 拍照巡检返回的绿/黄/橙/红风险等级含义与建议动作",
Content: "AI 巡检将蚕病风险分为四级:\n绿(0-30分):健康,正常巡检节奏,无需操作。\n黄(31-60分):需观察,增加巡检频次,人工复查、关注异常个体。\n橙(61-80分):疑似异常,触发分子检测任务(如 LAMP),按系统指引取样送检。\n红(81-100分):高度疑似/已发病,立即隔离病蚕、启动消毒,并触发专家会诊。\n\nAI 结果仅为筛查参考,最终确诊以分子检测(LAMP/qPCR)和专家诊断为准。",
SortOrder: 1,
},
}
- Step 2: 运行测试确认通过
Run: cd server-go && go test ./internal/model/ -run TestSeed -v
Expected: 4 个测试全部 PASS
- Step 3: Commit
git add server-go/internal/model/knowledge_seed.go
git commit -m "feat(knowledge): 蚕病百科与 AI 解读种子数据"
Task 3: Go handler、路由、权限、迁移
Files:
- Create:
server-go/internal/handler/knowledge.go - Modify:
server-go/internal/model/permission_seed.go(新增 2 条权限 + 角色映射) - Modify:
server-go/internal/database/db.go(AutoMigrate 注册 +seedKnowledge) - Modify:
server-go/cmd/server/main.go(注册RegisterKnowledgeRoutes)
Interfaces:
- Consumes: Task 1-2 的
model.Disease/model.KnowledgeArticle/ 种子变量;handler.bindUpdates(现有)。 - Produces: API
GET/POST/PATCH/DELETE /knowledge/diseases(/:id)、GET/POST/PATCH/DELETE /knowledge/articles(/:id);权限码knowledge:read/knowledge:write。
server-go/internal/handler/knowledge.go 完整实现:
package handler
import (
"net/http"
"silk-server-go/internal/middleware"
"silk-server-go/internal/model"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// RegisterKnowledgeRoutes 注册知识库路由
func RegisterKnowledgeRoutes(rg *gin.RouterGroup, db *gorm.DB) {
read := middleware.RequirePermission(db, "knowledge:read")
write := middleware.RequirePermission(db, "knowledge:write")
rg.GET("/knowledge/diseases", read, listDiseases(db))
rg.GET("/knowledge/diseases/:id", read, getDisease(db))
rg.POST("/knowledge/diseases", write, createDisease(db))
rg.PATCH("/knowledge/diseases/:id", write, updateDisease(db))
rg.DELETE("/knowledge/diseases/:id", write, deleteDisease(db))
rg.GET("/knowledge/articles", read, listArticles(db))
rg.GET("/knowledge/articles/:id", read, getArticle(db))
rg.POST("/knowledge/articles", write, createArticle(db))
rg.PATCH("/knowledge/articles/:id", write, updateArticle(db))
rg.DELETE("/knowledge/articles/:id", write, deleteArticle(db))
}
func listDiseases(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
q := db.Model(&model.Disease{})
if cat := c.Query("category"); cat != "" {
q = q.Where("category = ?", cat)
}
if kw := c.Query("keyword"); kw != "" {
q = q.Where("name ILIKE ?", "%"+kw+"%")
}
if enabled := c.Query("enabled"); enabled != "" {
q = q.Where("enabled = ?", enabled == "true")
}
var list []model.Disease
q.Order("sort_order ASC, name ASC").Find(&list)
c.JSON(http.StatusOK, list)
}
}
func getDisease(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var d model.Disease
if db.Where("id = ?", c.Param("id")).First(&d).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"})
return
}
c.JSON(http.StatusOK, d)
}
}
func createDisease(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var d model.Disease
if err := c.ShouldBindJSON(&d); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
d.ID = ""
if d.Name == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "病种名称不能为空"})
return
}
if err := db.Create(&d).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"})
return
}
c.JSON(http.StatusCreated, d)
}
}
func updateDisease(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var d model.Disease
if db.Where("id = ?", id).First(&d).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"})
return
}
updates, err := bindUpdates(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if len(updates) > 0 {
db.Model(&model.Disease{}).Where("id = ?", id).Updates(updates)
}
db.Where("id = ?", id).First(&d)
c.JSON(http.StatusOK, d)
}
}
func deleteDisease(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var d model.Disease
if db.Where("id = ?", id).First(&d).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "disease not found"})
return
}
db.Where("id = ?", id).Delete(&model.Disease{})
c.JSON(http.StatusOK, gin.H{"id": id})
}
}
func listArticles(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
q := db.Model(&model.KnowledgeArticle{})
if kind := c.Query("kind"); kind != "" {
q = q.Where("kind = ?", kind)
}
if enabled := c.Query("enabled"); enabled != "" {
q = q.Where("enabled = ?", enabled == "true")
}
var list []model.KnowledgeArticle
q.Order("sort_order ASC, created_at DESC").Find(&list)
c.JSON(http.StatusOK, list)
}
}
func getArticle(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var a model.KnowledgeArticle
if db.Where("id = ?", c.Param("id")).First(&a).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "article not found"})
return
}
c.JSON(http.StatusOK, a)
}
}
func createArticle(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
var a model.KnowledgeArticle
if err := c.ShouldBindJSON(&a); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
a.ID = ""
if a.Kind == "" || a.Title == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "文章类型和标题不能为空"})
return
}
if err := db.Create(&a).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "创建失败"})
return
}
c.JSON(http.StatusCreated, a)
}
}
func updateArticle(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var a model.KnowledgeArticle
if db.Where("id = ?", id).First(&a).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "article not found"})
return
}
updates, err := bindUpdates(c)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
if len(updates) > 0 {
db.Model(&model.KnowledgeArticle{}).Where("id = ?", id).Updates(updates)
}
db.Where("id = ?", id).First(&a)
c.JSON(http.StatusOK, a)
}
}
func deleteArticle(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
var a model.KnowledgeArticle
if db.Where("id = ?", id).First(&a).Error != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "article not found"})
return
}
db.Where("id = ?", id).Delete(&model.KnowledgeArticle{})
c.JSON(http.StatusOK, gin.H{"id": id})
}
}
server-go/internal/model/permission_seed.go 追加:
{"knowledge:read", "知识库查看", "查看蚕病百科和知识文章"},
{"knowledge:write", "知识库管理", "新增、编辑、删除知识库内容"},
并在 RolePermissionMap:admin/operator 增加 "knowledge:read", "knowledge:write";viewer/farmer 增加 "knowledge:read"。
server-go/internal/database/db.go:
AutoMigrate列表追加&model.Disease{}, &model.KnowledgeArticle{},- 新增
seedKnowledge(db)(幂等,按name/kind+title判重,不存在才Create),并在Init中seedPermissions(db)之后调用。
server-go/cmd/server/main.go:在 RegisterStorageRoutes 后追加 handler.RegisterKnowledgeRoutes(api, db)。
- Step 1: 修改代码
- Step 2: 验证
Run: cd server-go && go build ./... && go vet ./... && go test ./...
Expected: exit 0,无错误
- Step 3: Commit
git add server-go/internal/handler/knowledge.go server-go/internal/model/permission_seed.go server-go/internal/database/db.go server-go/cmd/server/main.go
git commit -m "feat(knowledge): 知识库 API、权限与迁移"
Task 4: Web 后台知识库页
Files:
- Create:
web/src/dal/knowledge.ts - Create:
web/src/pages/Knowledge.tsx - Modify:
web/src/router.tsx(注册/knowledge,权限knowledge:read) - Modify:
web/src/layout/BasicLayout.tsx(菜单“知识库”,BookOutlined,权限knowledge:read)
Interfaces:
- Consumes: 后端
Disease/KnowledgeArticleJSON 字段(与 Go JSON tag 完全一致)。
web/src/dal/knowledge.ts:
import { get, post, patch, del } from '../api/http';
export interface Disease {
id: string;
name: string;
category: string;
pathogen?: string;
transmission?: string;
medium?: string;
incubation?: string;
symptoms?: string;
highRiskStage?: string;
highRiskCondition?: string;
lethalTime?: string;
spreadTrend?: string;
recurrence?: string;
detection?: string;
prevention?: string;
imageUrl?: string;
sortOrder?: number;
enabled?: boolean;
createdAt?: string;
updatedAt?: string;
}
export interface KnowledgeArticle {
id: string;
kind: string;
title: string;
summary?: string;
content?: string;
sortOrder?: number;
enabled?: boolean;
createdAt?: string;
updatedAt?: string;
}
export const listDiseases = (params?: any) => get<Disease[]>('/knowledge/diseases', { params });
export const getDisease = (id: string) => get<Disease>(`/knowledge/diseases/${id}`);
export const createDisease = (data: Partial<Disease>) => post<Disease>('/knowledge/diseases', data);
export const updateDisease = (id: string, data: Partial<Disease>) => patch<Disease>(`/knowledge/diseases/${id}`, data);
export const deleteDisease = (id: string) => del(`/knowledge/diseases/${id}`);
export const listArticles = (params?: any) => get<KnowledgeArticle[]>('/knowledge/articles', { params });
export const createArticle = (data: Partial<KnowledgeArticle>) => post<KnowledgeArticle>('/knowledge/articles', data);
export const updateArticle = (id: string, data: Partial<KnowledgeArticle>) => patch<KnowledgeArticle>(`/knowledge/articles/${id}`, data);
export const deleteArticle = (id: string) => del(`/knowledge/articles/${id}`);
web/src/pages/Knowledge.tsx:参照 web/src/pages/Silkworm.tsx 的 ProTable + Modal 风格:
-
页面顶部
Tabs:Tab1「蚕病百科」,Tab2「AI 结果解读」。 -
蚕病百科 Tab:ProTable(rowKey=id,序号列、名称、类别 Tag、高发阶段、排序、状态、操作列 编辑/删除),toolBar「新建病种」;编辑/新建 Modal 表单含全部字段(名称、类别 Select[viral/fungal/bacterial/protozoan/other]、病原、传播途径、传染媒介、潜伏期、主要症状、高发阶段、高发条件、致死时间、传播趋势、连发性特征、检测方法、防治指南 TextArea、排序、启用 Switch)。
-
AI 解读 Tab:ProTable(序号、标题、类型 Tag、摘要、排序、操作),编辑/新建 Modal 含 kind Select[ai_guide/lamp_guide/sers_guide/seasonal_tip]、标题、摘要、正文 TextArea、排序、启用。
-
类别中文映射:
{ viral: '病毒性', fungal: '真菌性', bacterial: '细菌性', protozoan: '原虫性', other: '其他' }。 -
写操作按钮仅当
authService.hasPermission('knowledge:write')时渲染;无权限时只读列表。 -
Step 1: 新增 dal 与页面,注册路由与菜单
-
Step 2: 验证
Run: cd web && npm run lint && npm run build
Expected: exit 0,无 lint 错误,tsc + vite 构建通过
- Step 3: Commit
git add web/src/dal/knowledge.ts web/src/pages/Knowledge.tsx web/src/router.tsx web/src/layout/BasicLayout.tsx
git commit -m "feat(web): 知识库页面(蚕病百科 + AI 解读)"
Task 5: 小程序知识库查看页
Files:
- Create:
miniapp/src/api/knowledge.ts - Modify:
miniapp/src/types/index.ts(追加Disease、KnowledgeArticle接口) - Create:
miniapp/src/pages/knowledge/index.tsx、index.config.ts、index.module.scss - Create:
miniapp/src/pages/knowledge/detail/index.tsx、detail/index.config.ts、detail/index.module.scss - Modify:
miniapp/src/pages/settings/index.tsx(“监控管理”菜单追加“知识库”入口) - Modify:
miniapp/src/app.config.ts(pages 追加两个页面)
Interfaces:
- Consumes: 后端
GET /knowledge/diseases、GET /knowledge/articles?kind=ai_guide。
miniapp/src/api/knowledge.ts:
import { get } from './request';
export interface Disease {
id: string;
name: string;
category: string;
pathogen?: string;
transmission?: string;
medium?: string;
incubation?: string;
symptoms?: string;
highRiskStage?: string;
highRiskCondition?: string;
lethalTime?: string;
spreadTrend?: string;
recurrence?: string;
detection?: string;
prevention?: string;
sortOrder?: number;
}
export interface KnowledgeArticle {
id: string;
kind: string;
title: string;
summary?: string;
content?: string;
sortOrder?: number;
}
export const listDiseases = () => get<Disease[]>('/knowledge/diseases');
export const listAIGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'ai_guide' });
页面行为:
-
pages/knowledge/index:页面标题“知识库”;顶部渲染 AI 结果解读卡片(listAIGuides取第一条,展示标题+摘要,点击navigateTo('/pages/knowledge/detail/index?kind=ai_guide&id=xx'));下方按类别分组渲染病种列表(类别中文 Tag + 病种名 + 症状摘要),点击进入detail?kind=disease&id=xx。 -
pages/knowledge/detail:按kind区分:disease用get<Disease>('/knowledge/diseases/'+id)渲染分段卡片(病原/传播途径/传染媒介/潜伏期/主要症状/高发阶段/高发条件/致死时间/传播趋势/连发性/检测方法/防治指南);ai_guide用get<KnowledgeArticle>('/knowledge/articles/'+id)渲染标题+正文(white-space: pre-wrap)。 -
样式参照
miniapp/src/pages/settings/index.module.scss的卡片风格。 -
settings/index.tsx的handleMenuTap增加case 'knowledge': Taro.navigateTo({ url: '/pages/knowledge/index' }); break;,菜单项文案“知识库”。 -
Step 1: 新增 api、类型与两个页面,注册路由与设置页入口
-
Step 2: 验证
Run: cd miniapp && npm run build:weapp
Expected: exit 0,miniapp/dist 重新生成(生成产物不提交)
- Step 3: Commit
git add miniapp/src
git commit -m "feat(miniapp): 知识库查看页"
Task 6: 全量验证与交付
- Step 1: 后端
Run: cd server-go && go test ./... && go build ./... && go vet ./...
- Step 2: Web
Run: cd web && npm run lint && npm run build
- Step 3: 小程序
Run: cd miniapp && npm run build:weapp
- Step 4: 检查变更范围
Run: git status --short,确认只包含知识库相关源文件,未动生成产物。
- Step 5: 交付说明
汇总:修改文件清单、验证输出、种子数据说明(9 病种 + 1 AI 文章)、风险提示(handler 端到端 API 需 PostgreSQL 运行环境联调;前端测试基建未配置,属于计划 #27)。