|
|
|
@@ -0,0 +1,239 @@
|
|
|
|
|
import { Router, type Request, type Response, type Router as RouterType } from 'express';
|
|
|
|
|
import { eq, like, and, desc, count } from 'drizzle-orm';
|
|
|
|
|
import { db } from '../config/database.js';
|
|
|
|
|
import { userHeadphoneImpedances } from '../schemas/index.js';
|
|
|
|
|
import logger from '../config/logger.js';
|
|
|
|
|
import { ApiResponse } from '../utils/response.js';
|
|
|
|
|
import { authMiddleware } from '../middleware/auth.js';
|
|
|
|
|
import { HeadphoneImpedanceCreateSchema, HeadphoneImpedanceUpdateSchema } from '../validators/headphoneImpedance.js';
|
|
|
|
|
|
|
|
|
|
const router: RouterType = Router();
|
|
|
|
|
|
|
|
|
|
// 所有接口需登录
|
|
|
|
|
router.use('/api/headphone-impedance', authMiddleware);
|
|
|
|
|
|
|
|
|
|
function toDict(item: typeof userHeadphoneImpedances.$inferSelect) {
|
|
|
|
|
return {
|
|
|
|
|
id: item.id,
|
|
|
|
|
mac_addr: item.macAddr,
|
|
|
|
|
device_model: item.deviceModel,
|
|
|
|
|
impedance_ohm: item.impedanceOhm,
|
|
|
|
|
headphone_brand: item.headphoneBrand,
|
|
|
|
|
headphone_model: item.headphoneModel,
|
|
|
|
|
headphone_brand_norm: item.headphoneBrandNorm,
|
|
|
|
|
headphone_model_norm: item.headphoneModelNorm,
|
|
|
|
|
ip_addr: item.ipAddr,
|
|
|
|
|
create_at: item.createAt ? item.createAt.toISOString() : null,
|
|
|
|
|
update_at: item.updateAt ? item.updateAt.toISOString() : null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** norm 字段生成规则:原始值 trim + 小写(与设备端上报行为一致) */
|
|
|
|
|
function norm(value: string) {
|
|
|
|
|
return value.trim().toLowerCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GET /api/headphone-impedance/ —— 分页列表(支持 mac/型号/品牌/耳机型号筛选)
|
|
|
|
|
router.get('/api/headphone-impedance/', async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const skip = parseInt(req.query.skip as string || '0', 10);
|
|
|
|
|
const limit = Math.min(parseInt(req.query.limit as string || '100', 10), 1000);
|
|
|
|
|
const macAddr = req.query.mac_addr as string | undefined;
|
|
|
|
|
const deviceModel = req.query.device_model as string | undefined;
|
|
|
|
|
const headphoneBrand = req.query.headphone_brand as string | undefined;
|
|
|
|
|
const headphoneModel = req.query.headphone_model as string | undefined;
|
|
|
|
|
|
|
|
|
|
const conditions = [];
|
|
|
|
|
if (macAddr) conditions.push(like(userHeadphoneImpedances.macAddr, `%${macAddr}%`));
|
|
|
|
|
if (deviceModel) conditions.push(eq(userHeadphoneImpedances.deviceModel, deviceModel));
|
|
|
|
|
// 品牌筛选对原始值与 norm 值分别模糊匹配(输入小写时也能命中大写原始值)
|
|
|
|
|
if (headphoneBrand) {
|
|
|
|
|
conditions.push(
|
|
|
|
|
like(userHeadphoneImpedances.headphoneBrand, `%${headphoneBrand}%`),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (headphoneModel) {
|
|
|
|
|
conditions.push(
|
|
|
|
|
like(userHeadphoneImpedances.headphoneModel, `%${headphoneModel}%`),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const whereClause = conditions.length > 0 ? and(...conditions) : undefined;
|
|
|
|
|
|
|
|
|
|
const [totalResult] = await db
|
|
|
|
|
.select({ value: count() })
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(whereClause);
|
|
|
|
|
const total = totalResult?.value ?? 0;
|
|
|
|
|
|
|
|
|
|
const rows = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(whereClause)
|
|
|
|
|
.orderBy(desc(userHeadphoneImpedances.id))
|
|
|
|
|
.offset(skip)
|
|
|
|
|
.limit(limit);
|
|
|
|
|
|
|
|
|
|
logger.info(`Found ${rows.length} headphone impedance records, total=${total}`);
|
|
|
|
|
|
|
|
|
|
if (!rows.length) {
|
|
|
|
|
res.json(ApiResponse.noData('empty'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res.json(ApiResponse.success({ items: rows.map(toDict), total, skip, limit }));
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
logger.error(`Error getting headphone impedance list: ${msg}`);
|
|
|
|
|
res.json(ApiResponse.error('error'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// GET /api/headphone-impedance/:id
|
|
|
|
|
router.get('/api/headphone-impedance/:id', async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const id = parseInt(req.params.id as string, 10);
|
|
|
|
|
logger.info(`Getting headphone impedance: id=${id}`);
|
|
|
|
|
|
|
|
|
|
const [item] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(eq(userHeadphoneImpedances.id, id))
|
|
|
|
|
.limit(1);
|
|
|
|
|
if (!item) {
|
|
|
|
|
logger.warn(`Headphone impedance not found: id=${id}`);
|
|
|
|
|
res.json(ApiResponse.noData('empty'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
res.json(ApiResponse.success(toDict(item)));
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
logger.error(`Error getting headphone impedance: ${msg}`);
|
|
|
|
|
res.json(ApiResponse.error('error'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// POST /api/headphone-impedance/
|
|
|
|
|
router.post('/api/headphone-impedance/', async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const parsed = HeadphoneImpedanceCreateSchema.safeParse(req.body);
|
|
|
|
|
if (!parsed.success) {
|
|
|
|
|
const errors = parsed.error.issues.map((e: { message: string }) => e.message).join('; ');
|
|
|
|
|
res.json(ApiResponse.error(errors));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = parsed.data;
|
|
|
|
|
logger.info(`Creating headphone impedance: mac=${data.mac_addr}, model=${data.device_model}, ohm=${data.impedance_ohm}`);
|
|
|
|
|
|
|
|
|
|
const result = await db.insert(userHeadphoneImpedances).values({
|
|
|
|
|
macAddr: data.mac_addr.trim(),
|
|
|
|
|
deviceModel: data.device_model.trim(),
|
|
|
|
|
impedanceOhm: data.impedance_ohm,
|
|
|
|
|
headphoneBrand: data.headphone_brand.trim(),
|
|
|
|
|
headphoneModel: data.headphone_model.trim(),
|
|
|
|
|
headphoneBrandNorm: norm(data.headphone_brand),
|
|
|
|
|
headphoneModelNorm: norm(data.headphone_model),
|
|
|
|
|
ipAddr: data.ip_addr ?? '',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const [created] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(eq(userHeadphoneImpedances.id, result[0].insertId))
|
|
|
|
|
.limit(1);
|
|
|
|
|
logger.info(`Headphone impedance created: id=${created.id}`);
|
|
|
|
|
res.json(ApiResponse.success(toDict(created)));
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
logger.error(`Error creating headphone impedance: ${msg}`);
|
|
|
|
|
res.json(ApiResponse.error('error'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// PUT /api/headphone-impedance/:id
|
|
|
|
|
router.put('/api/headphone-impedance/:id', async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const id = parseInt(req.params.id as string, 10);
|
|
|
|
|
|
|
|
|
|
const parsed = HeadphoneImpedanceUpdateSchema.safeParse(req.body);
|
|
|
|
|
if (!parsed.success) {
|
|
|
|
|
const errors = parsed.error.issues.map((e: { message: string }) => e.message).join('; ');
|
|
|
|
|
res.json(ApiResponse.error(errors));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = parsed.data;
|
|
|
|
|
logger.info(`Updating headphone impedance: id=${id}`);
|
|
|
|
|
|
|
|
|
|
const [item] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(eq(userHeadphoneImpedances.id, id))
|
|
|
|
|
.limit(1);
|
|
|
|
|
if (!item) {
|
|
|
|
|
logger.warn(`Headphone impedance not found: id=${id}`);
|
|
|
|
|
res.json(ApiResponse.noData('empty'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const updateData: Partial<typeof userHeadphoneImpedances.$inferInsert> = {};
|
|
|
|
|
if (data.mac_addr !== undefined && data.mac_addr !== null) updateData.macAddr = data.mac_addr.trim();
|
|
|
|
|
if (data.device_model !== undefined && data.device_model !== null) updateData.deviceModel = data.device_model.trim();
|
|
|
|
|
if (data.impedance_ohm !== undefined && data.impedance_ohm !== null) updateData.impedanceOhm = data.impedance_ohm;
|
|
|
|
|
if (data.headphone_brand !== undefined && data.headphone_brand !== null) {
|
|
|
|
|
updateData.headphoneBrand = data.headphone_brand.trim();
|
|
|
|
|
updateData.headphoneBrandNorm = norm(data.headphone_brand);
|
|
|
|
|
}
|
|
|
|
|
if (data.headphone_model !== undefined && data.headphone_model !== null) {
|
|
|
|
|
updateData.headphoneModel = data.headphone_model.trim();
|
|
|
|
|
updateData.headphoneModelNorm = norm(data.headphone_model);
|
|
|
|
|
}
|
|
|
|
|
if (data.ip_addr !== undefined && data.ip_addr !== null) updateData.ipAddr = data.ip_addr;
|
|
|
|
|
|
|
|
|
|
if (Object.keys(updateData).length > 0) {
|
|
|
|
|
await db.update(userHeadphoneImpedances).set(updateData).where(eq(userHeadphoneImpedances.id, id));
|
|
|
|
|
}
|
|
|
|
|
logger.info(`Headphone impedance updated: id=${id}`);
|
|
|
|
|
|
|
|
|
|
const [updated] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(eq(userHeadphoneImpedances.id, id))
|
|
|
|
|
.limit(1);
|
|
|
|
|
res.json(ApiResponse.success(toDict(updated)));
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
logger.error(`Error updating headphone impedance: ${msg}`);
|
|
|
|
|
res.json(ApiResponse.error('error'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// DELETE /api/headphone-impedance/:id
|
|
|
|
|
router.delete('/api/headphone-impedance/:id', async (req: Request, res: Response) => {
|
|
|
|
|
try {
|
|
|
|
|
const id = parseInt(req.params.id as string, 10);
|
|
|
|
|
logger.info(`Deleting headphone impedance: id=${id}`);
|
|
|
|
|
|
|
|
|
|
const [item] = await db
|
|
|
|
|
.select()
|
|
|
|
|
.from(userHeadphoneImpedances)
|
|
|
|
|
.where(eq(userHeadphoneImpedances.id, id))
|
|
|
|
|
.limit(1);
|
|
|
|
|
if (!item) {
|
|
|
|
|
logger.warn(`Headphone impedance not found: id=${id}`);
|
|
|
|
|
res.json(ApiResponse.noData('empty'));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await db.delete(userHeadphoneImpedances).where(eq(userHeadphoneImpedances.id, id));
|
|
|
|
|
logger.info(`Headphone impedance deleted: id=${id}`);
|
|
|
|
|
res.json(ApiResponse.success(null, 'success'));
|
|
|
|
|
} catch (e: unknown) {
|
|
|
|
|
const msg = e instanceof Error ? e.message : String(e);
|
|
|
|
|
logger.error(`Error deleting headphone impedance: ${msg}`);
|
|
|
|
|
res.json(ApiResponse.error('error'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export default router;
|