feat(api): add RESTful endpoints and SSE streams for context and docgov lifecycle
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+451
-28
@@ -4,8 +4,12 @@ import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"chat-agent-system/internal/bizdev"
|
||||
"chat-agent-system/internal/ctxmgr"
|
||||
"chat-agent-system/internal/docgov"
|
||||
)
|
||||
|
||||
// HealthResponse 健康检查响应
|
||||
@@ -29,20 +33,32 @@ type ChatRequest struct {
|
||||
WorkDir string `json:"work_dir,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
UserRole string `json:"user_role,omitempty"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
ProjectID int64 `json:"project_id,omitempty"`
|
||||
}
|
||||
|
||||
// TaskRunRequestDTO 独立任务运行请求
|
||||
type TaskRunRequestDTO struct {
|
||||
TaskKey string `json:"task_key"`
|
||||
Stage string `json:"stage,omitempty"` // 若指定 stage,则执行成熟度阶段跃迁
|
||||
ProjectName string `json:"project_name"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
DeviceType string `json:"device_type"`
|
||||
IntendedUse string `json:"intended_use"`
|
||||
TargetMarket string `json:"target_market"`
|
||||
Fields map[string]string `json:"fields,omitempty"`
|
||||
Backend string `json:"backend,omitempty"`
|
||||
AutoRepair bool `json:"auto_repair"`
|
||||
TaskKey string `json:"task_key"`
|
||||
Stage string `json:"stage,omitempty"` // 若指定 stage,则执行成熟度阶段跃迁
|
||||
ProjectName string `json:"project_name"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
DeviceType string `json:"device_type"`
|
||||
IntendedUse string `json:"intended_use"`
|
||||
TargetMarket string `json:"target_market"`
|
||||
Fields map[string]string `json:"fields,omitempty"`
|
||||
Backend string `json:"backend,omitempty"`
|
||||
AutoRepair bool `json:"auto_repair"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
ProjectID int64 `json:"project_id,omitempty"`
|
||||
DocID string `json:"doc_id,omitempty"`
|
||||
AuthorID int64 `json:"author_id,omitempty"`
|
||||
AuthorName string `json:"author_name,omitempty"`
|
||||
ReviewerID int64 `json:"reviewer_id,omitempty"`
|
||||
ReviewerName string `json:"reviewer_name,omitempty"`
|
||||
ApproverID int64 `json:"approver_id,omitempty"`
|
||||
ApproverName string `json:"approver_name,omitempty"`
|
||||
ForceDirectEdit bool `json:"force_direct_edit,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -117,15 +133,25 @@ func (s *Server) handleRunTask(w http.ResponseWriter, r *http.Request) {
|
||||
// 1. 如果指定了 stage,则执行成熟度阶段编排
|
||||
if req.Stage != "" {
|
||||
stageRes, err := s.svc.ExecuteStage(r.Context(), bizdev.StageRunRequest{
|
||||
Stage: bizdev.MaturityStage(req.Stage),
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
Stage: bizdev.MaturityStage(req.Stage),
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
UserID: req.UserID,
|
||||
ProjectID: req.ProjectID,
|
||||
DocID: req.DocID,
|
||||
AuthorID: req.AuthorID,
|
||||
AuthorName: req.AuthorName,
|
||||
ReviewerID: req.ReviewerID,
|
||||
ReviewerName: req.ReviewerName,
|
||||
ApproverID: req.ApproverID,
|
||||
ApproverName: req.ApproverName,
|
||||
ForceDirectEdit: req.ForceDirectEdit,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "execute stage failed: "+err.Error(), http.StatusInternalServerError)
|
||||
@@ -140,15 +166,25 @@ func (s *Server) handleRunTask(w http.ResponseWriter, r *http.Request) {
|
||||
req.TaskKey = "intake"
|
||||
}
|
||||
taskRes, err := s.svc.ExecuteTask(r.Context(), bizdev.TaskRunRequest{
|
||||
TaskKey: req.TaskKey,
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
TaskKey: req.TaskKey,
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
UserID: req.UserID,
|
||||
ProjectID: req.ProjectID,
|
||||
DocID: req.DocID,
|
||||
AuthorID: req.AuthorID,
|
||||
AuthorName: req.AuthorName,
|
||||
ReviewerID: req.ReviewerID,
|
||||
ReviewerName: req.ReviewerName,
|
||||
ApproverID: req.ApproverID,
|
||||
ApproverName: req.ApproverName,
|
||||
ForceDirectEdit: req.ForceDirectEdit,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "execute task failed: "+err.Error(), http.StatusInternalServerError)
|
||||
@@ -168,3 +204,390 @@ func writeJSONResponse(w http.ResponseWriter, status int, data any) {
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 多级上下文与偏好记忆 RESTful 控制器
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// handleGetUserContext 获取单用户私有上下文与个性化偏好
|
||||
func (s *Server) handleGetUserContext(w http.ResponseWriter, r *http.Request) {
|
||||
userIDStr := r.URL.Query().Get("user_id")
|
||||
projectIDStr := r.URL.Query().Get("project_id")
|
||||
sessionID := r.URL.Query().Get("session_id")
|
||||
|
||||
userID, _ := strconv.ParseInt(userIDStr, 10, 64)
|
||||
if userID <= 0 {
|
||||
userID = 1
|
||||
}
|
||||
projectID, _ := strconv.ParseInt(projectIDStr, 10, 64)
|
||||
|
||||
if s.svc == nil || s.svc.ContextManager() == nil {
|
||||
http.Error(w, "context manager not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
uCtx, err := s.svc.ContextManager().GetUserContext(r.Context(), projectID, userID, sessionID)
|
||||
if err != nil {
|
||||
http.Error(w, "get user context failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, uCtx)
|
||||
}
|
||||
|
||||
// UpdateUserPreferenceRequest 更新用户工作偏好请求
|
||||
type UpdateUserPreferenceRequest struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
CommunicationStyle ctxmgr.CommunicationStyle `json:"communication_style"`
|
||||
FocusAreas []string `json:"focus_areas"`
|
||||
OutputFormatPref string `json:"output_format_pref"`
|
||||
CustomInstructions string `json:"custom_instructions"`
|
||||
}
|
||||
|
||||
// handleUpdateUserPreference 定期整理或前端保存用户习惯偏好
|
||||
func (s *Server) handleUpdateUserPreference(w http.ResponseWriter, r *http.Request) {
|
||||
var req UpdateUserPreferenceRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.UserID <= 0 {
|
||||
http.Error(w, "user_id must be positive", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.ContextManager() == nil {
|
||||
http.Error(w, "context manager not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
pref := &ctxmgr.UserPreference{
|
||||
UserID: req.UserID,
|
||||
CommunicationStyle: req.CommunicationStyle,
|
||||
FocusAreas: req.FocusAreas,
|
||||
OutputFormatPref: req.OutputFormatPref,
|
||||
CustomInstructions: req.CustomInstructions,
|
||||
}
|
||||
|
||||
if err := s.svc.ContextManager().UpdateUserPreference(r.Context(), pref); err != nil {
|
||||
http.Error(w, "save user preference failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, map[string]any{"status": "ok", "user_id": req.UserID})
|
||||
}
|
||||
|
||||
// handleGetProjectContext 获取项目级多方协同事实黑板与里程碑决策流
|
||||
func (s *Server) handleGetProjectContext(w http.ResponseWriter, r *http.Request) {
|
||||
projectIDStr := r.URL.Query().Get("project_id")
|
||||
projectID, _ := strconv.ParseInt(projectIDStr, 10, 64)
|
||||
if projectID <= 0 {
|
||||
http.Error(w, "missing or invalid project_id query param", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.ContextManager() == nil {
|
||||
http.Error(w, "context manager not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
pCtx, err := s.svc.ContextManager().GetProjectContext(r.Context(), projectID)
|
||||
if err != nil {
|
||||
http.Error(w, "get project context failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, pCtx)
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 受控文档编制、审核、批准与认领生命周期 RESTful 控制器
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// handleDocGovGetDocument 查询指定受控文档详情与治理状态
|
||||
func (s *Server) handleDocGovGetDocument(w http.ResponseWriter, r *http.Request) {
|
||||
docID := r.URL.Query().Get("doc_id")
|
||||
if docID == "" {
|
||||
http.Error(w, "missing doc_id query param", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := s.svc.DocGov().GetDocument(r.Context(), docID)
|
||||
if err != nil {
|
||||
http.Error(w, "get document failed: "+err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, doc)
|
||||
}
|
||||
|
||||
// DocGovClaimRequest 认领文档请求
|
||||
type DocGovClaimRequest struct {
|
||||
DocID string `json:"doc_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
UserRole string `json:"user_role"` // author, reviewer, approver
|
||||
}
|
||||
|
||||
// handleDocGovClaim 查阅并认领受控文档 (非模板状态查看方可认领)
|
||||
func (s *Server) handleDocGovClaim(w http.ResponseWriter, r *http.Request) {
|
||||
var req DocGovClaimRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.DocID == "" {
|
||||
http.Error(w, "doc_id cannot be empty", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := s.svc.DocGov().RecordDocumentViewed(r.Context(), req.DocID, req.UserID, docgov.UserRole(req.UserRole), req.UserName)
|
||||
if err != nil {
|
||||
http.Error(w, "claim document failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, doc)
|
||||
}
|
||||
|
||||
// DocGovSubmitReviewRequest 送审请求
|
||||
type DocGovSubmitReviewRequest struct {
|
||||
DocID string `json:"doc_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
}
|
||||
|
||||
// handleDocGovSubmitReview 编制人将文档送审 (drafting -> reviewing, 正文受控锁定)
|
||||
func (s *Server) handleDocGovSubmitReview(w http.ResponseWriter, r *http.Request) {
|
||||
var req DocGovSubmitReviewRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
doc, err := s.svc.DocGov().SubmitForReview(r.Context(), req.DocID, req.UserID, docgov.RoleAuthor, req.UserName)
|
||||
if err != nil {
|
||||
http.Error(w, "submit review failed: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, doc)
|
||||
}
|
||||
|
||||
// DocGovDecisionRequest 审核/批准决议请求
|
||||
type DocGovDecisionRequest struct {
|
||||
DocID string `json:"doc_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Action string `json:"action"` // approve 或 reject
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Reason string `json:"reason,omitempty"` // 驳回时必填
|
||||
RollbackTo string `json:"rollback_to,omitempty"` // 批准人驳回时可选: drafting 或 reviewing
|
||||
}
|
||||
|
||||
// handleDocGovReviewDecision 审核人决议 (通过流转至 approving,驳回必须填理由并回退 drafting 解锁)
|
||||
func (s *Server) handleDocGovReviewDecision(w http.ResponseWriter, r *http.Request) {
|
||||
var req DocGovDecisionRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
action := strings.ToLower(strings.TrimSpace(req.Action))
|
||||
var doc *docgov.DocumentRecord
|
||||
var err error
|
||||
|
||||
if action == "approve" {
|
||||
doc, err = s.svc.DocGov().ReviewApprove(r.Context(), req.DocID, req.UserID, docgov.RoleReviewer, req.UserName, req.Comment)
|
||||
} else if action == "reject" {
|
||||
reason := req.Reason
|
||||
if reason == "" {
|
||||
reason = req.Comment
|
||||
}
|
||||
doc, err = s.svc.DocGov().ReviewReject(r.Context(), req.DocID, req.UserID, docgov.RoleReviewer, req.UserName, reason)
|
||||
} else {
|
||||
http.Error(w, "invalid action: must be approve or reject", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "review decision failed: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, doc)
|
||||
}
|
||||
|
||||
// handleDocGovApproverDecision 批准人决议 (通过流转至 approved 终态归档,驳回必填理由并回退)
|
||||
func (s *Server) handleDocGovApproverDecision(w http.ResponseWriter, r *http.Request) {
|
||||
var req DocGovDecisionRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
action := strings.ToLower(strings.TrimSpace(req.Action))
|
||||
var doc *docgov.DocumentRecord
|
||||
var err error
|
||||
|
||||
if action == "approve" {
|
||||
doc, err = s.svc.DocGov().ApproverApprove(r.Context(), req.DocID, req.UserID, docgov.RoleApprover, req.UserName, req.Comment)
|
||||
} else if action == "reject" {
|
||||
reason := req.Reason
|
||||
if reason == "" {
|
||||
reason = req.Comment
|
||||
}
|
||||
rollbackState := docgov.DocumentState(req.RollbackTo)
|
||||
doc, err = s.svc.DocGov().ApproverReject(r.Context(), req.DocID, req.UserID, docgov.RoleApprover, req.UserName, reason, rollbackState)
|
||||
} else {
|
||||
http.Error(w, "invalid action: must be approve or reject", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "approver decision failed: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, doc)
|
||||
}
|
||||
|
||||
// DecideProposalRequest 拍板变更提案请求
|
||||
type DecideProposalRequest struct {
|
||||
ProposalID string `json:"proposal_id"`
|
||||
AuthorID int64 `json:"author_id"`
|
||||
Approve bool `json:"approve"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
|
||||
// handleDocGovDecideProposal 编制负责人拍板决定是否采纳变更提案
|
||||
func (s *Server) handleDocGovDecideProposal(w http.ResponseWriter, r *http.Request) {
|
||||
var req DecideProposalRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
proposal, err := s.svc.DocGov().DecideModificationProposal(r.Context(), req.ProposalID, req.AuthorID, req.Approve, req.Reason)
|
||||
if err != nil {
|
||||
http.Error(w, "decide proposal failed: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, proposal)
|
||||
}
|
||||
|
||||
// handleDocGovListComments 查询批注与修改建议清单
|
||||
func (s *Server) handleDocGovListComments(w http.ResponseWriter, r *http.Request) {
|
||||
docID := r.URL.Query().Get("doc_id")
|
||||
if docID == "" {
|
||||
http.Error(w, "missing doc_id query param", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
comments, err := s.svc.DocGov().ListComments(r.Context(), docID)
|
||||
if err != nil {
|
||||
http.Error(w, "list comments failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, comments)
|
||||
}
|
||||
|
||||
// AddCommentRequestDTO 新增批注请求
|
||||
type AddCommentRequestDTO struct {
|
||||
DocID string `json:"doc_id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
UserRole string `json:"user_role"`
|
||||
TargetSection string `json:"target_section,omitempty"`
|
||||
CommentType string `json:"comment_type,omitempty"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// handleDocGovAddComment 审核人/批准人/编制人新增章节批注或修改建议
|
||||
func (s *Server) handleDocGovAddComment(w http.ResponseWriter, r *http.Request) {
|
||||
var req AddCommentRequestDTO
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
cType := docgov.CommentType(req.CommentType)
|
||||
if cType == "" {
|
||||
cType = docgov.CommentGeneral
|
||||
}
|
||||
|
||||
comment, err := s.svc.DocGov().AddComment(
|
||||
r.Context(), req.DocID, req.UserID, req.UserName,
|
||||
docgov.UserRole(req.UserRole), req.TargetSection, cType, req.Content,
|
||||
)
|
||||
if err != nil {
|
||||
http.Error(w, "add comment failed: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, comment)
|
||||
}
|
||||
|
||||
// handleDocGovListAuditLogs 查询文档全生命周期审计轨迹流
|
||||
func (s *Server) handleDocGovListAuditLogs(w http.ResponseWriter, r *http.Request) {
|
||||
docID := r.URL.Query().Get("doc_id")
|
||||
if docID == "" {
|
||||
http.Error(w, "missing doc_id query param", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if s.svc == nil || s.svc.DocGov() == nil {
|
||||
http.Error(w, "docgov engine not initialized", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
|
||||
logs, err := s.svc.DocGov().ListAuditLogs(r.Context(), docID)
|
||||
if err != nil {
|
||||
http.Error(w, "list audit logs failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writeJSONResponse(w, http.StatusOK, logs)
|
||||
}
|
||||
|
||||
@@ -70,6 +70,22 @@ func (s *Server) Routes() http.Handler {
|
||||
mux.HandleFunc("POST /api/v1/tasks/run/stream", s.handleRunTaskStream)
|
||||
mux.HandleFunc("GET /api/v1/tasks/run/stream", s.handleRunTaskStream)
|
||||
|
||||
// 6. 多级上下文与偏好记忆管理
|
||||
mux.HandleFunc("GET /api/v1/context/user", s.handleGetUserContext)
|
||||
mux.HandleFunc("PUT /api/v1/context/user/preference", s.handleUpdateUserPreference)
|
||||
mux.HandleFunc("GET /api/v1/context/project", s.handleGetProjectContext)
|
||||
|
||||
// 7. 受控文档编制、审核、批准生命周期与认领治理
|
||||
mux.HandleFunc("GET /api/v1/docgov/document", s.handleDocGovGetDocument)
|
||||
mux.HandleFunc("POST /api/v1/docgov/claim", s.handleDocGovClaim)
|
||||
mux.HandleFunc("POST /api/v1/docgov/submit-review", s.handleDocGovSubmitReview)
|
||||
mux.HandleFunc("POST /api/v1/docgov/review-decision", s.handleDocGovReviewDecision)
|
||||
mux.HandleFunc("POST /api/v1/docgov/approver-decision", s.handleDocGovApproverDecision)
|
||||
mux.HandleFunc("POST /api/v1/docgov/proposal/decide", s.handleDocGovDecideProposal)
|
||||
mux.HandleFunc("GET /api/v1/docgov/comments", s.handleDocGovListComments)
|
||||
mux.HandleFunc("POST /api/v1/docgov/comments", s.handleDocGovAddComment)
|
||||
mux.HandleFunc("GET /api/v1/docgov/audit-logs", s.handleDocGovListAuditLogs)
|
||||
|
||||
return s.corsMiddleware(mux)
|
||||
}
|
||||
|
||||
|
||||
+40
-18
@@ -25,6 +25,8 @@ type StreamChatRequest struct {
|
||||
WorkDir string `json:"work_dir,omitempty"`
|
||||
UserName string `json:"user_name,omitempty"`
|
||||
UserRole string `json:"user_role,omitempty"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
ProjectID int64 `json:"project_id,omitempty"`
|
||||
}
|
||||
|
||||
func (s *Server) handleChatStream(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -165,15 +167,25 @@ func (s *Server) handleRunTaskStream(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if req.Stage != "" {
|
||||
_, err := s.svc.ExecuteStage(ctxWithS, bizdev.StageRunRequest{
|
||||
Stage: bizdev.MaturityStage(req.Stage),
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
Stage: bizdev.MaturityStage(req.Stage),
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
UserID: req.UserID,
|
||||
ProjectID: req.ProjectID,
|
||||
DocID: req.DocID,
|
||||
AuthorID: req.AuthorID,
|
||||
AuthorName: req.AuthorName,
|
||||
ReviewerID: req.ReviewerID,
|
||||
ReviewerName: req.ReviewerName,
|
||||
ApproverID: req.ApproverID,
|
||||
ApproverName: req.ApproverName,
|
||||
ForceDirectEdit: req.ForceDirectEdit,
|
||||
})
|
||||
if err != nil {
|
||||
streamer.EmitError(err)
|
||||
@@ -188,15 +200,25 @@ func (s *Server) handleRunTaskStream(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
_, err := s.svc.ExecuteTask(ctxWithS, bizdev.TaskRunRequest{
|
||||
TaskKey: req.TaskKey,
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
TaskKey: req.TaskKey,
|
||||
ProjectName: req.ProjectName,
|
||||
CustomerName: req.CustomerName,
|
||||
DeviceType: req.DeviceType,
|
||||
IntendedUse: req.IntendedUse,
|
||||
TargetMarket: req.TargetMarket,
|
||||
Fields: req.Fields,
|
||||
Backend: req.Backend,
|
||||
AutoRepair: req.AutoRepair,
|
||||
UserID: req.UserID,
|
||||
ProjectID: req.ProjectID,
|
||||
DocID: req.DocID,
|
||||
AuthorID: req.AuthorID,
|
||||
AuthorName: req.AuthorName,
|
||||
ReviewerID: req.ReviewerID,
|
||||
ReviewerName: req.ReviewerName,
|
||||
ApproverID: req.ApproverID,
|
||||
ApproverName: req.ApproverName,
|
||||
ForceDirectEdit: req.ForceDirectEdit,
|
||||
})
|
||||
if err != nil {
|
||||
streamer.EmitError(err)
|
||||
|
||||
Reference in New Issue
Block a user