# AppController Code Review Remediation Design

## Goal

Resolve the actionable findings from `docs/code-review-standards.md` without splitting `AppController` in this phase. Preserve all existing `/app/v1/**` routes and successful response payloads.

## Scope

This phase includes:

- complete and safe global exception handling;
- removal of raw collection types and wildcard imports in `AppController`;
- removal of the identified device/schedule N+1 queries;
- safe image upload validation with a 20 MB endpoint limit;
- HTTP-level and unit regression tests for the changed behavior.

This phase does not include:

- splitting `AppController` into domain-specific controllers;
- changing MQTT topic contracts;
- changing database tables;
- renaming or removing existing HTTP routes.

## Exception Handling

`AppController` continues to let business and system exceptions propagate to `GlobalExceptionHandler`. The four local MQTT notification catches remain because notification failure is a deliberate degradation path after the primary database operation. Each retained catch must include the exception object in the log call.

`GlobalExceptionHandler` applies these rules:

- `ServiceException`: resolve the existing i18n key, log the request URI and full exception at `WARN`, and return the business message and existing business code.
- `BaseException`: use its already localized message, log the request URI and full exception at `WARN`, and return the business message.
- `RuntimeException` and `Exception`: log the request URI and full exception at `ERROR`, but return the standard `R.fail()` response so internal SQL, Redis, OSS, or network details are not exposed.

The response body contract remains `R<T>`. No new HTTP status mapping is introduced in this phase.

## Type Safety And Controller Cleanup

All raw `Map`, `List`, and response generic declarations in `AppController` are replaced with concrete generic types. Wildcard imports are replaced with explicit imports. The unused `AppScheduleServiceImpl` dependency is removed so the Controller depends only on service interfaces.

Repeated `SimpleDateFormat` creation in `AppController` is replaced with a shared immutable `DateTimeFormatter`. Commented-out annotations and dead commented code in touched sections are removed.

## Batch Query Design

Two batch query capabilities are added behind service interfaces:

1. `IAppDeviceService.queryByDeviceNos(Collection<String> deviceNos)` returns device VOs for the requested device numbers in one database query.
2. `IAppSchedulingDeviceService.findBoundDeviceNos(Collection<String> deviceNos)` returns the subset of device numbers that have schedule bindings in one database query.

`scheduleDeviceList` loads the current user's devices once, submits their device numbers to `findBoundDeviceNos`, and filters in memory. This replaces one binding query per device.

`getScheduleInfo` loads schedule bindings once, extracts device numbers, and calls `queryByDeviceNos` once. This replaces one device query per binding.

Schedule notification helpers load schedule details once per HTTP operation and reuse the resulting payload details for every bound device. Device-specific payload fields remain unchanged.

No unbounded all-table query is introduced: every new batch query is constrained by the device numbers already associated with the current request or current user.

## Image Upload Validation

`/app/v1/uploadImage` enforces all of the following before calling OSS:

- file is present and non-empty;
- file size is at most 20 MB;
- original filename has an allowed extension;
- declared `Content-Type` is an allowed image MIME type;
- file signature resolves to an allowed image type;
- extension, MIME type, and detected type are mutually compatible.

Allowed formats are JPEG (`jpg`, `jpeg`), PNG, GIF, WebP, and BMP. SVG is rejected because it is active content and cannot be safely accepted using only binary image validation.

The general Spring multipart limit remains 100 MB because other upload endpoints may need it. The 20 MB limit is local to the image endpoint.

## Testing

Implementation follows red-green-refactor.

Tests cover:

- unexpected Controller exceptions reach the global handler and return a generic message rather than the original exception message;
- `ServiceException` and `BaseException` retain their client-facing business messages;
- oversized, forged, unsupported, and valid image uploads;
- `scheduleDeviceList` performs one batch binding lookup and no per-device lookup;
- `getScheduleInfo` performs one batch device lookup and no per-binding lookup;
- schedule notification payload construction queries details once for multiple devices;
- existing AppController behavior remains green.

Verification commands:

```powershell
mvn -pl water-common/water-common-web -am "-DskipTests=false" "-Dmaven.test.skip=false" test
mvn -pl water-modules/water-app -am "-DskipTests=false" "-Dmaven.test.skip=false" "-Dprofiles.active=dev" test
git diff --check
```

## Compatibility And Rollback

The change preserves route paths, request field names, successful response payloads, MQTT topics, and database schema. The intentional observable change is that unexpected system exceptions no longer expose their original messages to clients.

The work is separable into exception handling, batch queries, image validation, and type cleanup. Each part can be reverted independently if a regression is found.
