kra-new/docs/audit/parity-ops-content.md

196 lines
20 KiB
Markdown

# GVA Operations and Content API Parity Audit
## Scope and method
- **Reference:** official GVA working tree `C:\Users\Yvan\AppData\Local\Temp\gva-parity-02f3783`, commit `02f37833255e0e339c3d69199cb5a468f17de9fc`.
- **Local target:** this repository's Gin compatibility surface. The global private/public placement and the compatibility operation-record selection are established in `internal/server/gin.go:33-48` and `internal/server/middleware/audit.go:137-178`.
- **Checked for each endpoint:** method/path, public vs private placement, operation-record placement, request binding/defaults, business side effects, and response/error envelope. Both applications use a `200` JSON response envelope for business failures (`internal/server/httpx/response.go:28-36`); this audit treats message-only wording differences as compatible unless the control flow or payload differs.
- **Deliberately excluded:** GVA auto-code, AI, skills, example, plugin-management and other modules that have not been migrated. This includes the GVA error-log action which delegates to `AutoCodeService.LLMAuto`; it is listed below only so the route inventory is complete, not as a required migration gap.
`OK` means no material mismatch was found in this read-only audit. `EXCLUDED` is a GVA route intentionally outside the migration scope. The route evidence in the tables is the local router shown first, then the GVA router.
## Findings
### Excluded - Error-solution endpoint is intentionally out of scope
`GET /sysError/getSysErrorSolution?id=<id>` is registered as a private, operation-recorded GVA route in `server/router/system/sys_error.go:15-24`, seeded in both its API and Casbin sources (`server/source/system/api.go:221-225`, `server/source/system/casbin.go:294-298`), and consumed by the GVA UI (`web/src/api/system/sysError.js:120-126`, `web/src/view/systemTools/sysError/sysError.vue:290-293`). It validates `id`, changes the error to `处理中`, starts an asynchronous LLM solution task through `AutoCodeService.LLMAuto`, and returns `已提交至AI处理` (`server/api/v1/system/sys_error.go:179-197`; delegation and state transitions at `server/service/system/sys_error.go:83-129`).
The local error router exposes only delete/update/find/list/create (`internal/server/router/audit.go:27-33`) and neither `internal/server/handler/audit.go` nor `internal/server/middleware/audit.go:167-171` defines the action/audit selection. The local client module likewise ends after `getSysErrorList` (`web/src/api/system/sysError.js:82-96`), while GVA exports the action at `web/src/api/system/sysError.js:120-126`. This is an expected absence under the user's explicit exclusion of AI/code-generation functionality. It does not count as a parity defect for the scoped migration; do not add its route, Casbin seed, client action, asynchronous state transition, or LLM integration unless that exclusion changes.
### Cross-cutting audit behavior is aligned
The selected mutation set matches GVA's per-router `OperationRecord()` use: local `operationRoutes` includes the dictionary, parameter, config, token, export, version, error, login, data-access, timed-task, announcement and email mutations (`internal/server/middleware/audit.go:161-171`). Both implementations reuse the access-log capture, mask the same sensitive JSON fields, preserve the multipart/download truncation markers, and record request/trace/device metadata (`internal/server/middleware/audit.go:27-103`; GVA `server/middleware/access_log.go:43-112`, `server/middleware/operation.go:20-102`). No parity difference was found at this boundary; keep it as a regression-test seam rather than a finding.
## Per-module endpoint review
### Dictionary and dictionary detail
Route sets match GVA (`internal/server/router/dictionary.go:9-29`; GVA `server/router/system/sys_dictionary.go:14-23`, `sys_dictionary_detail.go:14-24`). Local handlers bind the same JSON/query shapes and return the same compatibility wrapper keys, including `resysDictionary`, `reSysDictionaryDetail`, `list`, and `path` (`internal/server/handler/dictionary.go:20-266`). The data layer keeps GVA's `name OR type` search, unpaged dictionary-list behavior, detail filters and `sort,id` ordering (`internal/data/dictionary.go:218-282`, `370-400`); GVA equivalents are `server/service/system/sys_dictionary.go:122-149` and `sys_dictionary_detail.go:180-209`.
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /sysDictionary/createSysDictionary` | OK | JSON dictionary; uniqueness/create; detailed created response. |
| `PUT /sysDictionary/updateSysDictionary` | OK | JSON dictionary; update response. |
| `DELETE /sysDictionary/deleteSysDictionary` | OK | JSON dictionary ID; deletion side effects retained. |
| `GET /sysDictionary/findSysDictionary` | OK | Query dictionary; enabled lookup; `{resysDictionary: ...}`. |
| `GET /sysDictionary/getSysDictionaryList` | OK | `name` searches name/type; unpaged list. |
| `GET /sysDictionary/getSysDictionaryListWithDetails` | OK | `name` searches name/type; details preloaded and sorted. |
| `GET /sysDictionary/exportSysDictionary` | OK | Query `ID` required; export shape retained. |
| `POST /sysDictionary/importSysDictionary` | OK | JSON `{json}` import and failure propagation. |
| `POST /sysDictionaryDetail/createSysDictionaryDetail` | OK | JSON detail; hierarchy metadata calculated. |
| `PUT /sysDictionaryDetail/updateSysDictionaryDetail` | OK | JSON detail; hierarchy update/cycle validation retained. |
| `DELETE /sysDictionaryDetail/deleteSysDictionaryDetail` | OK | JSON detail ID; parent-with-children rejection retained. |
| `GET /sysDictionaryDetail/findSysDictionaryDetail` | OK | Query `ID`; `{reSysDictionaryDetail: ...}`. |
| `GET /sysDictionaryDetail/getSysDictionaryDetailList` | OK | Query page/filter fields `label,value,status,sysDictionaryID,parentID,level`; `sort,id` ordering. |
| `GET /sysDictionaryDetail/getDictionaryTreeList` | OK | Query `sysDictionaryID` required; root tree with children. |
| `GET /sysDictionaryDetail/getDictionaryTreeListByType` | OK | Query `type` required; type-resolved root tree. |
| `GET /sysDictionaryDetail/getDictionaryDetailsByParent` | OK | `sysDictionaryID` required, optional `parentID`/`includeChildren`. |
| `GET /sysDictionaryDetail/getDictionaryPath` | OK | Query `id` required; ancestor path. |
### System parameters
Route, binding and result shapes match (`internal/server/router/parameter.go:9-19`; GVA `server/router/system/sys_params.go:15-23`; local handler `internal/server/handler/parameter.go:19-92`; GVA API `server/api/v1/system/sys_params.go:22-169`). `name`, `key`, created-time window and page fields reach the local repo in the same way (`internal/service/parameter.go:20-64`, `internal/data/parameter.go:63-90`).
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /sysParams/createSysParams` | OK | Required JSON `name,key,value`; create. |
| `PUT /sysParams/updateSysParams` | OK | Required JSON `name,key,value`; struct-update behavior retained. |
| `DELETE /sysParams/deleteSysParams` | OK | Query `ID`; delete one. |
| `DELETE /sysParams/deleteSysParamsByIds` | OK | Query `IDs[]`; batch delete. |
| `GET /sysParams/findSysParams` | OK | Query `ID`; one parameter. |
| `GET /sysParams/getSysParam` | OK | Query `key`; one parameter. |
| `GET /sysParams/getSysParamsList` | OK | Query page/name/key/created-time window; page result. |
### Security config, system config/info/reload, and initialization
The route split and mutation audit placement match GVA: local `internal/server/router/system_config.go:9-21` and `router/public.go:9-16`; GVA `server/router/system/sys_security_config.go:15-18`, `sys_system.go:15-20`, `sys_initdb.go:12-13`. The system config read already returns the required `{config: ...}` payload because `ConfigurationJSON()` constructs that envelope (`internal/data/config_management.go:30-117`), matching GVA `SysConfigResponse` (`server/model/system/response/sys_system.go:5-7`). Local reload also reloads the task runtime (`internal/biz/system_init.go:66-73`), preserving GVA-visible reload behavior while avoiding a process restart.
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `GET /securityConfig/getSecurityConfig` | OK | Private read; security config response fields. |
| `POST /securityConfig/setSecurityConfig` | OK | Private JSON update; saved config returned. |
| `POST /system/getSystemConfig` | OK | Private read; `{config: ...}` wrapper and secret masking. |
| `POST /system/setSystemConfig` | OK | Private JSON `config`; persistent config update. |
| `POST /system/reloadSystem` | OK | Private mutation; reloads runtime/task config. |
| `POST /system/getServerInfo` | OK | Private server information under `server`. |
| `POST /init/checkdb` | OK | Public `needInit` response and compatible message. |
| `POST /init/initdb` | OK | Public only while uninitialized; validates DB input and seeds active Gin routes. |
### API token, export templates, and versions
Paths and methods match (`internal/server/router/api_token.go:9-15`, `export.go:9-23`, `version.go:9-18`; GVA `sys_api_token.go:15-17`, `sys_export_template.go:18-33`, `sys_version.go:15-23`). API-token revocation also blacklists the persisted JWT before disabling it (`internal/data/api_token.go:123-129`), matching GVA `server/service/system/sys_api_token.go:93-106`. Export token issuance/consumption and public downloads are present in the local handler (`internal/server/handler/export.go:144-225`), and version import/export preserves staged errors (`internal/server/handler/version.go:85-151`, `internal/service/version.go:185-267`).
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /sysApiToken/createApiToken` | OK | JSON `userId,authorityId,days,remark`; authority check/JWT issuance. |
| `POST /sysApiToken/getApiTokenList` | OK | JSON page/user/status filters; newest-first page result. |
| `POST /sysApiToken/deleteApiToken` | OK | JSON ID; blacklist then mark disabled. |
| `POST /sysExportTemplate/createSysExportTemplate` | OK | JSON template; name required/create. |
| `PUT /sysExportTemplate/updateSysExportTemplate` | OK | JSON template; name required/update. |
| `DELETE /sysExportTemplate/deleteSysExportTemplate` | OK | JSON ID; empty-ID failure preserved. |
| `DELETE /sysExportTemplate/deleteSysExportTemplateByIds` | OK | JSON IDs; batch delete. |
| `GET /sysExportTemplate/findSysExportTemplate` | OK | Query ID; `resysExportTemplate` key. |
| `GET /sysExportTemplate/getSysExportTemplateList` | OK | Query filters/page; page result. |
| `GET /sysExportTemplate/previewSQL` | OK | `templateID` required plus encoded `params`; SQL preview. |
| `GET /sysExportTemplate/exportExcel` | OK | `templateID` plus params; one-time download-token URL. |
| `GET /sysExportTemplate/exportTemplate` | OK | Blank-template token URL. |
| `POST /sysExportTemplate/importExcel` | OK | Multipart file plus query `templateID`; import. |
| `GET /sysExportTemplate/exportExcelByToken` | OK | Public token consumption; XLSX download. |
| `GET /sysExportTemplate/exportTemplateByToken` | OK | Public blank-template token consumption; XLSX download. |
| `DELETE /sysVersion/deleteSysVersion` | OK | Query `ID`; delete. |
| `DELETE /sysVersion/deleteSysVersionByIds` | OK | Query `IDs[]`; batch delete. |
| `GET /sysVersion/findSysVersion` | OK | Query `ID`; version record. |
| `GET /sysVersion/getSysVersionList` | OK | Query page/name/code/date window; page result. |
| `POST /sysVersion/exportVersion` | OK | Required JSON version name/code and selected menu/API/dictionary data. |
| `GET /sysVersion/downloadVersionJson` | OK | Query ID; attachment JSON stream. |
| `POST /sysVersion/importVersion` | OK | JSON bundle; staged menu/API/dictionary import errors. |
### Operation/login/data-access/error/file logs
The non-AI endpoints and their public/private placement match local `internal/server/router/audit.go:9-33` with GVA `sys_operation_record.go:12-15`, `sys_login_log.go:16-21`, `sys_data_access_log.go:14-17`, `sys_error.go:16-26`, and `sys_log_viewer.go:10-12`. Local log-viewer validation rejects malformed month/date/path before storage access (`internal/server/handler/audit.go:199-271`) and passes the expected errors. Error, operation and login handler bindings are in `internal/server/handler/audit.go:67-343`.
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `GET /sysOperationRecord/getSysOperationRecordList` | OK | Query page/path/method/status; newest-first page result. |
| `GET /sysOperationRecord/findSysOperationRecord` | OK | Query ID; `reSysOperationRecord` wrapper. |
| `DELETE /sysOperationRecord/deleteSysOperationRecord` | OK | JSON ID; delete one. |
| `DELETE /sysOperationRecord/deleteSysOperationRecordByIds` | OK | JSON IDs; batch delete. |
| `GET /sysLoginLog/getLoginLogList` | OK | Query page/username/status; newest-first page result. |
| `GET /sysLoginLog/findLoginLog` | OK | Query ID; login record. |
| `DELETE /sysLoginLog/deleteLoginLog` | OK | JSON ID; delete one. |
| `DELETE /sysLoginLog/deleteLoginLogByIds` | OK | JSON IDs; batch delete. |
| `POST /dataAccessLog/getDataAccessLogList` | OK | JSON page/eventType/targetTable; page result. |
| `DELETE /dataAccessLog/deleteDataAccessLogByIds` | OK | JSON IDs; batch delete. |
| `GET /logViewer/dates` | OK | Required query `month`; log-date listing. |
| `GET /logViewer/files` | OK | Required query `date`; file listing. |
| `GET /logViewer/content` | OK | Required `date,path`, optional cursor; safe content page. |
| `POST /sysError/createSysError` | OK | Public JSON error record creation. |
| `DELETE /sysError/deleteSysError` | OK | Query `ID`; delete one. |
| `DELETE /sysError/deleteSysErrorByIds` | OK | Query `IDs[]`; batch delete. |
| `PUT /sysError/updateSysError` | OK | JSON error record update. |
| `GET /sysError/findSysError` | OK | Query `ID`; error record. |
| `GET /sysError/getSysErrorList` | OK | Query page/form/info/date range; page result. |
| `GET /sysError/getSysErrorSolution` | EXCLUDED | GVA private async AI solution submit through `AutoCodeService.LLMAuto`; outside the requested migration scope. |
### Timed task and SSE
All nine routes match (`internal/server/router/task.go:9-20`; GVA `server/router/system/sys_timed_task.go:15-26`). Local mutation handlers bind the same task/ID payloads (`internal/server/handler/task.go:24-120`) and drive durable task changes plus scheduler schedule/remove actions (`internal/biz/task.go:157-193`). The local SSE handler has the GVA-required long-lived headers, initial comment, task alert event, heartbeat and deadline removal (`internal/server/handler/task.go:122-161`); GVA calls out the timeout exception at `server/router/system/sys_timed_task.go:24-26`.
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /timedTask/createTimedTask` | OK | JSON task; validate, persist and schedule. |
| `PUT /timedTask/updateTimedTask` | OK | JSON task; validate, persist and reschedule. |
| `DELETE /timedTask/deleteTimedTask` | OK | JSON required ID; remove schedule then delete. |
| `POST /timedTask/toggleTimedTask` | OK | JSON ID/enabled; persist toggle then reschedule. |
| `POST /timedTask/triggerTimedTask` | OK | JSON ID; manual asynchronous trigger. |
| `GET /timedTask/getTimedTaskList` | OK | Query page/name/executorType/enabled; next-run metadata. |
| `GET /timedTask/getTimedTaskLogList` | OK | Query page/taskId/status; page result. |
| `GET /timedTask/getRegisteredMethods` | OK | Registered method catalog under `methods`. |
| `GET /timedTask/alertStream` | OK | Authenticated SSE `timedTask:alert` stream with heartbeat. |
### Media uploads, attachment categories, and chunked upload
Route sets match local `internal/server/router/media.go:9-30` and GVA `server/router/media/media_file_upload_and_download.go:12-19`, `media_attachment_category.go:12-14`, `media_upload.go:10-13`. Local handler bindings follow the GVA APIs (`internal/server/handler/media.go:19-244`; GVA `server/api/v1/media/media_file_upload_download.go:25-216`, `media_upload.go:26-116`). The storage/repository paths retain extension validation, key reference counting on delete, partial batch-delete reporting, attachment-category tree behavior, ownership checks, chunk MD5/full-file MD5, atomic merge claim and cleanup (`internal/biz/media.go:51-170`, `internal/biz/media_upload.go:36-184`, `internal/data/media.go:128-203`).
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /fileUploadAndDownload/upload` | OK | Multipart `file`, `classId`, query `noSave`; upload/create response `file`. |
| `POST /fileUploadAndDownload/getFileList` | OK | JSON page/keyword/classId/tag/user/time/order filters. |
| `POST /fileUploadAndDownload/deleteFile` | OK | JSON ID; storage delete only for final key reference. |
| `POST /fileUploadAndDownload/deleteFiles` | OK | JSON IDs; partial success reports `failedIds`. |
| `GET /fileUploadAndDownload/findFile` | OK | Query `id`; file response. |
| `POST /fileUploadAndDownload/editFileName` | OK | JSON ID/name; existence check then rename. |
| `POST /fileUploadAndDownload/importURL` | OK | JSON file array; batch insert. |
| `POST /fileUploadAndDownload/listOssFiles` | OK | JSON prefix/cursor/limit; list/nextCursor/hasMore. |
| `GET /attachmentCategory/getCategoryList` | OK | Category tree rooted at PID 0. |
| `POST /attachmentCategory/addCategory` | OK | JSON category; sibling uniqueness plus create/update. |
| `POST /attachmentCategory/deleteCategory` | OK | JSON ID required; reject non-leaf category. |
| `POST /mediaUpload/init` | OK | JSON init; max size, instant/resume probing. |
| `POST /mediaUpload/chunk` | OK | Multipart `uploadId,chunkIndex,chunkHash,chunk`; owner/status/MD5 checks. |
| `POST /mediaUpload/complete` | OK | JSON `uploadId`; atomic merge, full hash check, media registration. |
| `DELETE /mediaUpload/:uploadId` | OK | Path ID; owner-only session/chunk cleanup. |
### Announcement/info and email
Announcement route placement matches GVA: private mutations/reads and public datasource/public message (`internal/server/router/announcement.go:9-24`; GVA `server/plugin/announcement/router/info.go:16-29`). The local public response intentionally mirrors GVA's fixed compatibility message (`internal/server/handler/announcement.go:97-99`; GVA `server/plugin/announcement/api/info.go:171-181`). Announcement CRUD/time-range/data-source semantics are retained (`internal/server/handler/announcement.go:23-95`, `internal/data/announcement.go:38-98`; GVA `server/plugin/announcement/service/info.go:15-76`). Email paths, JSON binding and send/test behavior match (`internal/server/router/email.go:9-13`, `internal/server/handler/email.go:15-34`; GVA `server/plugin/email/router/sys_email.go:16-17`, `api/sys_email.go:20-51`).
| Endpoint | Status | Binding / effect check |
| --- | --- | --- |
| `POST /info/createInfo` | OK | Private JSON title/content/user/attachments; create. |
| `DELETE /info/deleteInfo` | OK | Private query `ID`; delete one. |
| `DELETE /info/deleteInfoByIds` | OK | Private query `IDs[]`; batch delete. |
| `PUT /info/updateInfo` | OK | Private JSON record; update. |
| `GET /info/findInfo` | OK | Private query `ID`; record response. |
| `GET /info/getInfoList` | OK | Private query page/created-time window; page result. |
| `GET /info/getInfoDataSource` | OK | Public `userID` label/value datasource. |
| `GET /info/getInfoPublic` | OK | Public fixed compatibility message, not a listing API. |
| `POST /email/emailTest` | OK | Private configured-recipient test mail. |
| `POST /email/sendEmail` | OK | Private JSON `to,subject,body`; sends mail. |
## Suggested regression tests
1. Add a route contract test that enumerates every in-scope table entry above under both no-prefix and a configured `routerPrefix`; omit the documented AI-excluded `GET /sysError/getSysErrorSolution`.
2. Add cross-implementation request fixtures for the high-risk shapes: dictionary-detail hierarchy, export token single use/type mismatch, API-token blacklist revoke, media partial delete/chunk ownership/hash, and timed-task SSE headers plus heartbeat/event format.
3. Snapshot the response envelope keys for special cases: `resysDictionary`, `reSysDictionaryDetail`, `reSysOperationRecord`, `resysExportTemplate`, system `{config: ...}`, and page `{list,total,page,pageSize}`.
4. Test audit persistence separately from API behavior: mutation-route selection, sensitive-field masking, multipart/download truncation, and the shared `requestId/traceId/deviceId` metadata.