## Context

The project already maps `as_asset_device` through `AssetDeviceEntity` and `AssetDeviceMapper`, but it has no device Controller, Service, DTOs, page, image upload path, route, or menu. The table keeps a device name, two optional image attachment fields, a logical `user_person_id`, two text status fields, and the common `delete_time` soft-delete marker.

Enterprise WeChat asset work is currently in progress in the same repository. Its files are already dirty, including the enterprise WeChat Controller, Service, DTOs, frontend module, CSS, and tests. Device management must therefore be independently buildable and defer the two shared frontend registrations until final integration.

There is no existing upload service. This change stores original image files locally and records controlled, application-relative file identifiers in `image_attachment_1` and `image_attachment_2`. The browser renders a fixed-size preview of the original image; it does not generate or store a second thumbnail file.

## Goals / Non-Goals

**Goals:**

- Provide a complete, safe device-asset CRUD workflow over existing `as_asset_device` rows.
- Support up to two JPG, PNG, or GIF images per device, with a 20 MB maximum for each image.
- Provide searchable company-person selection and readable person names in the list response.
- Keep active device names unique, apply approved dropdown values, and protect referenced devices from deletion.
- Add a dedicated Device Asset Management page, route, and menu without changing enterprise WeChat business behavior.
- Allow parallel implementation by isolating device work to new files and assigning final shared-file integration to one owner.

**Non-Goals:**

- Do not modify database schema, run migrations, or directly operate the database.
- Do not add device selection to the enterprise WeChat creation form; that is a later change.
- Do not generate physical thumbnail files, convert image formats, or add an object-storage dependency.
- Do not permanently remove original image files during normal device soft deletion.
- Do not introduce authentication or a general-purpose file-management module.

## Decisions

### Device API and data contract

Use `/api/device-assets` for list, creation, update, and deletion. `GET` accepts `page`, `size`, `deviceName`, `userPersonId`, `userUsageStatus`, and `assetRelationStatus`; it filters `delete_time = 0` and orders by descending ID. `POST` and `PUT /{id}` accept multipart form data so device fields and both optional image files are saved together. `DELETE /{id}` performs a soft deletion.

The list and detail response return `id`, device fields, original-image access URLs, `userPersonId`, `userPersonName`, and audit times, but never return `deleteTime` or the physical storage path. This follows the current asset API envelope and pagination shape.

### Validation and reference handling

`deviceName` is required and is unique among active records. Save operations reject values outside the fixed approved status lists. A selected `userPersonId` must resolve to a non-deleted company person; it can be omitted.

Before a device is soft-deleted, the service checks active `as_phone_asset`, `as_wecom_account`, `as_wechat_account`, and `as_douyin_account` rows for the device ID. If any exists, deletion fails with a readable list of reference sources. This preserves logical association integrity because the database intentionally has no foreign keys.

### Image storage and access

The server validates extension and image content for JPG, PNG, and GIF and limits each file to 20 MB. It saves each original file beneath a configurable local root, defaulting to `./uploads/device-assets`, with a generated opaque file identifier. Attachment database columns store only that identifier.

An application endpoint resolves an identifier to a file only after constraining it to the upload root; response DTOs expose the endpoint URL rather than a disk path. On update, omitted image parts preserve the current image; an explicit remove flag clears its database reference. A successful replacement saves the new file before updating the row, while failed requests clean up newly written temporary files. Soft deletion keeps files for recoverability.

Browser-sized preview was selected over server-generated thumbnail files because it adds no image-processing dependency or duplicate storage. The trade-off is that a list may download larger original files; lazy image loading will reduce initial page work.

### Frontend behavior

`DeviceAssetView` provides filters, a paged table, a create/edit dialog, remote company-person search, the two status dropdowns, two image selectors, inline browser previews, and a delete confirmation. Each slot supports retain, replace, or explicit removal during edit. The page sets `loading` and `saving` states and surfaces API errors through the existing Element Plus message pattern.

Use a dedicated `device-asset.css`, imported by the device module, rather than editing the currently modified shared stylesheet. The final integration adds the `设备资产管理` navigation item and maps `#/device-assets` to the new view.

### Parallel ownership and integration

Device work owns only newly added `DeviceAsset*` backend files, device frontend module files, device CSS, and device tests. It reuses but does not edit `AssetDeviceEntity`, `AssetDeviceMapper`, or enterprise WeChat code. Enterprise WeChat work retains ownership of all current dirty files. A single integration owner changes `App.js` and `router/index.js` after both feature branches are ready.

This ownership model avoids merge conflicts. The device branch is developed in a separate Git worktree from the current dirty enterprise-WeChat worktree; final integration runs all relevant tests after both changes are present.

## Risks / Trade-offs

- [Original files can be 20 MB] → Lazy-load list previews, display a loading state, and never generate duplicate thumbnail files.
- [Local disk is not shared across multiple application instances] → Keep the upload root configurable and document that a future multi-instance deployment must move to shared/object storage.
- [Soft deletion retains image files] → Retention is deliberate for recovery; any physical cleanup must be a separately authorized maintenance workflow.
- [Database has no foreign keys] → Service-level active-reference checks block deletion, and tests cover each referencing asset type.
- [User-provided file extension can be misleading] → Validate both allowed type and decodable image content; serve files only through opaque identifiers constrained to the upload root.
- [Shared menu and router files are collision points] → Make their two-line integration a separately owned final commit only.

## Migration Plan

1. Create the device feature branch/worktree from the agreed base without resetting or stashing the current enterprise-WeChat working tree.
2. Implement and test only device-owned new files; do not change enterprise-WeChat files, shared CSS, or shared navigation during this phase.
3. Configure the multipart request maximum to accommodate two 20 MB files plus form metadata, without changing database schema.
4. Deploy with a writable device upload directory and verify that its access endpoint can read only files under the configured root.
5. Run device API tests, frontend build, device Playwright tests, then the existing phone and enterprise-WeChat smoke tests after final integration.
6. Roll back code by reverting the device feature and route/menu integration commits. Existing rows and retained files remain recoverable; no database rollback is required.

## Open Questions

None. The approved status values, per-image 20 MB limit, browser-sized previews, local storage, deletion protection, route, menu label, and deferred enterprise-WeChat selection are all fixed for this change.
