This commit is contained in:
@@ -729,6 +729,9 @@ func ClaimDeviceHandler(service *saas.Service) http.Handler {
|
||||
if len(input.ClaimCode) < 16 || len(input.ClaimCode) > 128 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "claim_code", Message: "长度必须为16至128字符"})
|
||||
}
|
||||
if validUUID(input.ClaimCode) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "claim_code", Message: "不能填写设备编号,请输入设备屏幕或维护端生成的一次性绑定码"})
|
||||
}
|
||||
if !validUUID(input.HouseholdID) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "household_id", Message: "必须是UUID"})
|
||||
}
|
||||
@@ -766,6 +769,86 @@ func GetManagedDeviceHandler(service *saas.Service) http.Handler {
|
||||
})
|
||||
}
|
||||
|
||||
func GetDeviceBindingImpactHandler(service *saas.Service) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
principal, ok := resourcePrincipal(writer, request, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
action := strings.TrimSpace(request.URL.Query().Get("action"))
|
||||
targetSubjectID := strings.TrimSpace(request.URL.Query().Get("target_subject_id"))
|
||||
fieldErrors := make([]FieldError, 0)
|
||||
if action != saas.DeviceBindingActionUnbind && action != saas.DeviceBindingActionTransfer {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "action", Message: "必须是unbind或transfer"})
|
||||
}
|
||||
if action == saas.DeviceBindingActionTransfer && !validUUID(targetSubjectID) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "target_subject_id", Message: "转移时必须提供目标长者UUID"})
|
||||
}
|
||||
if len(fieldErrors) > 0 {
|
||||
WriteAPIError(writer, request, http.StatusBadRequest, Error{Code: "VALIDATION_FAILED", Message: "设备绑定影响检查参数不正确", FieldErrors: fieldErrors})
|
||||
return
|
||||
}
|
||||
result, err := service.GetDeviceBindingImpact(request.Context(), principal, request.PathValue("id"), action, targetSubjectID)
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
}
|
||||
writer.Header().Set("Cache-Control", "no-store")
|
||||
WriteJSON(writer, request, http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
||||
func ChangeDeviceBindingHandler(service *saas.Service) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
principal, ok := resourcePrincipal(writer, request, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Action string `json:"action"`
|
||||
ExpectedBindingID string `json:"expected_binding_id"`
|
||||
TargetSubjectID string `json:"target_subject_id"`
|
||||
Reason string `json:"reason"`
|
||||
Acknowledged bool `json:"acknowledged"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "设备绑定变更参数不正确") {
|
||||
return
|
||||
}
|
||||
input.Action = strings.TrimSpace(input.Action)
|
||||
input.ExpectedBindingID = strings.TrimSpace(input.ExpectedBindingID)
|
||||
input.TargetSubjectID = strings.TrimSpace(input.TargetSubjectID)
|
||||
input.Reason = strings.TrimSpace(input.Reason)
|
||||
fieldErrors := make([]FieldError, 0)
|
||||
if input.Action != saas.DeviceBindingActionUnbind && input.Action != saas.DeviceBindingActionTransfer {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "action", Message: "必须是unbind或transfer"})
|
||||
}
|
||||
if !validUUID(input.ExpectedBindingID) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "expected_binding_id", Message: "必须是影响检查返回的当前绑定UUID"})
|
||||
}
|
||||
if input.Action == saas.DeviceBindingActionTransfer && !validUUID(input.TargetSubjectID) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "target_subject_id", Message: "转移时必须提供目标长者UUID"})
|
||||
}
|
||||
if len(input.Reason) < 3 || len(input.Reason) > 300 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "reason", Message: "长度必须为3至300字符"})
|
||||
}
|
||||
if !input.Acknowledged {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "acknowledged", Message: "必须确认已核对影响范围"})
|
||||
}
|
||||
if len(fieldErrors) > 0 {
|
||||
WriteAPIError(writer, request, http.StatusBadRequest, Error{Code: "VALIDATION_FAILED", Message: "设备绑定变更参数不正确", FieldErrors: fieldErrors})
|
||||
return
|
||||
}
|
||||
result, err := service.ChangeDeviceBinding(request.Context(), principal, request.PathValue("id"), RequestID(request.Context()), saas.ChangeDeviceBindingInput{
|
||||
Action: input.Action, ExpectedBindingID: input.ExpectedBindingID, TargetSubjectID: input.TargetSubjectID, Reason: input.Reason, Acknowledged: input.Acknowledged,
|
||||
})
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
}
|
||||
WriteJSON(writer, request, http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
||||
func GetDevicePresenceHandler(service *saas.Service) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
principal, ok := resourcePrincipal(writer, request, "id")
|
||||
@@ -1009,21 +1092,104 @@ func PatchStoryHandler(service *saas.Service) http.Handler {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Content string `json:"content"`
|
||||
Reason string `json:"reason"`
|
||||
Content string `json:"content"`
|
||||
Reason string `json:"reason"`
|
||||
ErrorType string `json:"error_type"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "故事修订参数不正确") {
|
||||
return
|
||||
}
|
||||
input.Content = strings.TrimSpace(input.Content)
|
||||
input.Reason = strings.TrimSpace(input.Reason)
|
||||
input.ErrorType = strings.TrimSpace(input.ErrorType)
|
||||
if input.Content == "" || len([]rune(input.Content)) > 50000 || input.Reason == "" || len([]rune(input.Reason)) > 500 {
|
||||
writeValidation(writer, request, "故事修订参数不正确",
|
||||
FieldError{Field: "content", Message: "不能为空且最长50000字符"},
|
||||
FieldError{Field: "reason", Message: "不能为空且最长500字符"})
|
||||
return
|
||||
}
|
||||
result, err := service.ReviseStory(request.Context(), principal, request.PathValue("id"), input.Content, input.Reason, RequestID(request.Context()), expected)
|
||||
if input.ErrorType != "" && !saas.IsContentErrorType(input.ErrorType) {
|
||||
writeValidation(writer, request, "故事修订参数不正确", FieldError{Field: "error_type", Message: "质量问题类型不正确"})
|
||||
return
|
||||
}
|
||||
result, err := service.ReviseStory(request.Context(), principal, request.PathValue("id"), input.Content, input.Reason, RequestID(request.Context()), expected, input.ErrorType)
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
}
|
||||
writer.Header().Set("ETag", concurrency.FormatETag(result.Version))
|
||||
WriteJSON(writer, request, http.StatusOK, result)
|
||||
})
|
||||
}
|
||||
|
||||
func UpdateFactCandidateHandler(service *saas.Service) http.Handler {
|
||||
return http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
principal, ok := resourcePrincipal(writer, request, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !validUUID(request.PathValue("fact_id")) {
|
||||
writeValidation(writer, request, "事实候选编号不正确", FieldError{Field: "fact_id", Message: "必须是UUID"})
|
||||
return
|
||||
}
|
||||
expected, err := concurrency.ParseIfMatch(request)
|
||||
if err != nil {
|
||||
writeETagError(writer, request, err)
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
FactType *string `json:"fact_type"`
|
||||
Content *string `json:"content"`
|
||||
Sensitivity *string `json:"sensitivity"`
|
||||
Status *string `json:"status"`
|
||||
ErrorType *string `json:"error_type"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "事实候选审核参数不正确") {
|
||||
return
|
||||
}
|
||||
input.Reason = strings.TrimSpace(input.Reason)
|
||||
fieldErrors := make([]FieldError, 0)
|
||||
if input.Reason == "" || len([]rune(input.Reason)) > 500 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "reason", Message: "不能为空且最长500字符"})
|
||||
}
|
||||
if input.FactType != nil {
|
||||
value := strings.TrimSpace(*input.FactType)
|
||||
input.FactType = &value
|
||||
if value == "" || len(value) > 64 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "fact_type", Message: "不能为空且最长64字符"})
|
||||
}
|
||||
}
|
||||
if input.Content != nil {
|
||||
value := strings.TrimSpace(*input.Content)
|
||||
input.Content = &value
|
||||
if value == "" || len([]rune(value)) > 4000 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "content", Message: "不能为空且最长4000字符"})
|
||||
}
|
||||
}
|
||||
if input.Sensitivity != nil && *input.Sensitivity != "normal" && *input.Sensitivity != "sensitive" && *input.Sensitivity != "high" {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "sensitivity", Message: "必须为normal、sensitive或high"})
|
||||
}
|
||||
if input.Status != nil && *input.Status != "CANDIDATE" && *input.Status != "CONFIRMED" && *input.Status != "REJECTED" && *input.Status != "SUPERSEDED" {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "status", Message: "事实候选状态不正确"})
|
||||
}
|
||||
if input.ErrorType != nil {
|
||||
value := strings.TrimSpace(*input.ErrorType)
|
||||
input.ErrorType = &value
|
||||
if value == "" || !saas.IsContentErrorType(value) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "error_type", Message: "质量问题类型不正确"})
|
||||
}
|
||||
}
|
||||
if input.FactType == nil && input.Content == nil && input.Sensitivity == nil && input.Status == nil {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "body", Message: "至少提供一个需要修改或审核的字段"})
|
||||
}
|
||||
if len(fieldErrors) > 0 {
|
||||
WriteAPIError(writer, request, http.StatusBadRequest, Error{Code: "VALIDATION_FAILED", Message: "事实候选审核参数不正确", FieldErrors: fieldErrors})
|
||||
return
|
||||
}
|
||||
result, err := service.UpdateFactCandidate(request.Context(), principal, request.PathValue("id"), request.PathValue("fact_id"), RequestID(request.Context()), expected, saas.UpdateFactCandidateInput{
|
||||
FactType: input.FactType, Content: input.Content, Sensitivity: input.Sensitivity, Status: input.Status, ErrorType: input.ErrorType, Reason: input.Reason,
|
||||
})
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
@@ -1053,16 +1219,22 @@ func storyDecisionHandler(service *saas.Service, confirm bool) http.Handler {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Reason string `json:"reason"`
|
||||
Reason string `json:"reason"`
|
||||
ErrorType string `json:"error_type"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "故事审核参数不正确") {
|
||||
return
|
||||
}
|
||||
input.Reason = strings.TrimSpace(input.Reason)
|
||||
input.ErrorType = strings.TrimSpace(input.ErrorType)
|
||||
if input.Reason == "" || len([]rune(input.Reason)) > 500 {
|
||||
writeValidation(writer, request, "故事审核参数不正确", FieldError{Field: "reason", Message: "不能为空且最长500字符"})
|
||||
return
|
||||
}
|
||||
if input.ErrorType != "" && !saas.IsContentErrorType(input.ErrorType) {
|
||||
writeValidation(writer, request, "故事审核参数不正确", FieldError{Field: "error_type", Message: "质量问题类型不正确"})
|
||||
return
|
||||
}
|
||||
if confirm {
|
||||
story, memory, err := service.ConfirmStory(request.Context(), principal, request.PathValue("id"), input.Reason, RequestID(request.Context()), expected)
|
||||
if err != nil {
|
||||
@@ -1073,7 +1245,7 @@ func storyDecisionHandler(service *saas.Service, confirm bool) http.Handler {
|
||||
WriteJSON(writer, request, http.StatusOK, map[string]any{"story": story, "memory": memory})
|
||||
return
|
||||
}
|
||||
story, err := service.RejectStory(request.Context(), principal, request.PathValue("id"), input.Reason, RequestID(request.Context()), expected)
|
||||
story, err := service.RejectStory(request.Context(), principal, request.PathValue("id"), input.Reason, RequestID(request.Context()), expected, input.ErrorType)
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
@@ -1131,9 +1303,10 @@ func PatchMemoryHandler(service *saas.Service) http.Handler {
|
||||
return
|
||||
}
|
||||
var input struct {
|
||||
Content *string `json:"content"`
|
||||
Status *string `json:"status"`
|
||||
Reason string `json:"reason"`
|
||||
Content *string `json:"content"`
|
||||
Status *string `json:"status"`
|
||||
ErrorType *string `json:"error_type"`
|
||||
Reason string `json:"reason"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "记忆修改参数不正确") {
|
||||
return
|
||||
@@ -1153,6 +1326,13 @@ func PatchMemoryHandler(service *saas.Service) http.Handler {
|
||||
if input.Status != nil && !oneOf(*input.Status, "ACTIVE", "DISABLED") {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "status", Message: "只允许ACTIVE或DISABLED"})
|
||||
}
|
||||
if input.ErrorType != nil {
|
||||
value := strings.TrimSpace(*input.ErrorType)
|
||||
input.ErrorType = &value
|
||||
if value == "" || !saas.IsContentErrorType(value) {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "error_type", Message: "质量问题类型不正确"})
|
||||
}
|
||||
}
|
||||
if input.Reason == "" || len([]rune(input.Reason)) > 500 {
|
||||
fieldErrors = append(fieldErrors, FieldError{Field: "reason", Message: "不能为空且最长500字符"})
|
||||
}
|
||||
@@ -1160,7 +1340,7 @@ func PatchMemoryHandler(service *saas.Service) http.Handler {
|
||||
WriteAPIError(writer, request, http.StatusBadRequest, Error{Code: "VALIDATION_FAILED", Message: "记忆修改参数不正确", FieldErrors: fieldErrors})
|
||||
return
|
||||
}
|
||||
result, err := service.UpdateMemory(request.Context(), principal, request.PathValue("id"), RequestID(request.Context()), expected, saas.UpdateMemoryInput{Content: input.Content, Status: input.Status, Reason: input.Reason})
|
||||
result, err := service.UpdateMemory(request.Context(), principal, request.PathValue("id"), RequestID(request.Context()), expected, saas.UpdateMemoryInput{Content: input.Content, Status: input.Status, ErrorType: input.ErrorType, Reason: input.Reason})
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
return
|
||||
@@ -1233,15 +1413,23 @@ func CreatePrivacyRequestHandler(service *saas.Service) http.Handler {
|
||||
Reason string `json:"reason"`
|
||||
Confirmation string `json:"confirmation"`
|
||||
Scope struct {
|
||||
Categories []string `json:"categories"`
|
||||
Categories []string `json:"categories"`
|
||||
ConsentTypes []string `json:"consent_types"`
|
||||
Corrections struct {
|
||||
PreferredName *string `json:"preferred_name"`
|
||||
Language *string `json:"language"`
|
||||
Timezone *string `json:"timezone"`
|
||||
ProfileData *map[string]any `json:"profile_data"`
|
||||
ConversationPreferences *map[string]any `json:"conversation_preferences"`
|
||||
} `json:"corrections"`
|
||||
} `json:"scope"`
|
||||
}
|
||||
if !decodeWebJSON(writer, request, &input, "隐私请求参数不正确") {
|
||||
return
|
||||
}
|
||||
input.RequestType = strings.ToUpper(strings.TrimSpace(input.RequestType))
|
||||
if input.RequestType != "ACCESS" && input.RequestType != "DELETE" {
|
||||
writeValidation(writer, request, "隐私请求参数不正确", FieldError{Field: "request_type", Message: "必须为ACCESS或DELETE"})
|
||||
if input.RequestType != "ACCESS" && input.RequestType != "CORRECTION" && input.RequestType != "CONSENT_WITHDRAWAL" && input.RequestType != "DELETE" {
|
||||
writeValidation(writer, request, "隐私请求参数不正确", FieldError{Field: "request_type", Message: "必须为ACCESS、CORRECTION、CONSENT_WITHDRAWAL或DELETE"})
|
||||
return
|
||||
}
|
||||
if input.RequestType == "DELETE" {
|
||||
@@ -1274,6 +1462,68 @@ func CreatePrivacyRequestHandler(service *saas.Service) http.Handler {
|
||||
if len(categories) > 0 {
|
||||
scope["categories"] = categories
|
||||
}
|
||||
if input.RequestType == "CORRECTION" {
|
||||
corrections := map[string]any{}
|
||||
if input.Scope.Corrections.PreferredName != nil {
|
||||
value := strings.TrimSpace(*input.Scope.Corrections.PreferredName)
|
||||
if len([]rune(value)) < 1 || len([]rune(value)) > 120 {
|
||||
writeValidation(writer, request, "更正申请参数不正确", FieldError{Field: "scope.corrections.preferred_name", Message: "长度必须为1至120字符"})
|
||||
return
|
||||
}
|
||||
corrections["preferred_name"] = value
|
||||
}
|
||||
if input.Scope.Corrections.Language != nil {
|
||||
value := strings.TrimSpace(*input.Scope.Corrections.Language)
|
||||
if len(value) < 2 || len(value) > 20 {
|
||||
writeValidation(writer, request, "更正申请参数不正确", FieldError{Field: "scope.corrections.language", Message: "长度必须为2至20字符"})
|
||||
return
|
||||
}
|
||||
corrections["language"] = value
|
||||
}
|
||||
if input.Scope.Corrections.Timezone != nil {
|
||||
value := strings.TrimSpace(*input.Scope.Corrections.Timezone)
|
||||
if _, err := time.LoadLocation(value); err != nil {
|
||||
writeValidation(writer, request, "更正申请参数不正确", FieldError{Field: "scope.corrections.timezone", Message: "必须是有效IANA时区"})
|
||||
return
|
||||
}
|
||||
corrections["timezone"] = value
|
||||
}
|
||||
if input.Scope.Corrections.ProfileData != nil {
|
||||
corrections["profile_data"] = *input.Scope.Corrections.ProfileData
|
||||
}
|
||||
if input.Scope.Corrections.ConversationPreferences != nil {
|
||||
corrections["conversation_preferences"] = *input.Scope.Corrections.ConversationPreferences
|
||||
}
|
||||
if len(corrections) == 0 {
|
||||
writeValidation(writer, request, "更正申请参数不正确", FieldError{Field: "scope.corrections", Message: "至少提供一个需要更正的字段"})
|
||||
return
|
||||
}
|
||||
scope = map[string]any{"corrections": corrections}
|
||||
}
|
||||
if input.RequestType == "CONSENT_WITHDRAWAL" {
|
||||
allowedConsentTypes := map[string]bool{
|
||||
saas.ConsentTranscriptStorage: true, saas.ConsentRawAudioStorage: true, saas.ConsentStoryExtraction: true,
|
||||
saas.ConsentPersonalMemory: true, saas.ConsentFamilySharing: true,
|
||||
}
|
||||
seenConsentTypes := map[string]bool{}
|
||||
consentTypes := make([]string, 0, len(input.Scope.ConsentTypes))
|
||||
for index, raw := range input.Scope.ConsentTypes {
|
||||
consentType := strings.TrimSpace(raw)
|
||||
if !allowedConsentTypes[consentType] {
|
||||
writeValidation(writer, request, "授权撤回申请参数不正确", FieldError{Field: fmt.Sprintf("scope.consent_types[%d]", index), Message: "授权类型不正确"})
|
||||
return
|
||||
}
|
||||
if !seenConsentTypes[consentType] {
|
||||
seenConsentTypes[consentType] = true
|
||||
consentTypes = append(consentTypes, consentType)
|
||||
}
|
||||
}
|
||||
if len(consentTypes) == 0 {
|
||||
writeValidation(writer, request, "授权撤回申请参数不正确", FieldError{Field: "scope.consent_types", Message: "至少选择一种授权"})
|
||||
return
|
||||
}
|
||||
scope = map[string]any{"consent_types": consentTypes}
|
||||
}
|
||||
result, created, err := service.CreatePrivacyRequest(request.Context(), principal, request.PathValue("id"), idempotencyKey, RequestID(request.Context()), saas.CreatePrivacyRequestInput{RequestType: input.RequestType, Scope: scope, Reason: input.Reason, Confirmation: input.Confirmation})
|
||||
if err != nil {
|
||||
writeSaaSError(writer, request, err)
|
||||
@@ -1633,6 +1883,10 @@ func writeSaaSError(writer http.ResponseWriter, request *http.Request, err error
|
||||
WriteError(writer, request, http.StatusConflict, "DEVICE_CLAIM_ALREADY_USED", "设备绑定码已经用于其他绑定")
|
||||
case errors.Is(err, saas.ErrDeviceAlreadyBound):
|
||||
WriteError(writer, request, http.StatusConflict, "DEVICE_ALREADY_BOUND", "设备已经存在有效绑定")
|
||||
case errors.Is(err, saas.ErrDeviceBindingBlocked):
|
||||
WriteError(writer, request, http.StatusConflict, "DEVICE_BINDING_BLOCKED", "设备仍有活动通话、未完成命令或固件任务,请先处理阻断项")
|
||||
case errors.Is(err, saas.ErrDeviceBindingStale):
|
||||
WriteError(writer, request, http.StatusConflict, "DEVICE_BINDING_STALE", "设备绑定已发生变化,请重新执行影响检查")
|
||||
case errors.Is(err, saas.ErrIdempotencyConflict):
|
||||
WriteError(writer, request, http.StatusConflict, "IDEMPOTENCY_KEY_CONFLICT", "重复请求识别码已用于不同的请求内容")
|
||||
case errors.Is(err, saas.ErrSubjectDeleteConflict):
|
||||
@@ -1665,6 +1919,8 @@ func writeSaaSError(writer http.ResponseWriter, request *http.Request, err error
|
||||
WriteError(writer, request, http.StatusConflict, "PERSONALIZED_MEMORY_CONSENT_REQUIRED", "生成个性化记忆前必须取得当前有效授权")
|
||||
case errors.Is(err, saas.ErrReviewStateConflict):
|
||||
WriteError(writer, request, http.StatusConflict, "STORY_REVIEW_STATE_CONFLICT", "当前故事审核状态不允许此操作")
|
||||
case errors.Is(err, saas.ErrFactReviewStateConflict):
|
||||
WriteError(writer, request, http.StatusConflict, "FACT_REVIEW_STATE_CONFLICT", "当前事实候选状态不允许此操作")
|
||||
case errors.Is(err, pagination.ErrInvalidCursor):
|
||||
writeValidation(writer, request, "分页游标无效", FieldError{Field: "cursor", Message: "游标无效、已损坏或不是本服务签发"})
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user