feat(mobile,account-service): 统一发送服务 + 员工目录接口 + 好友申请五态/验证消息(B-52 施工 2/3)
- message_send_service:所有消息走统一门禁(登录/连接/空接收方/好友校验), 失败打印 SDK 原始 code 并映射中文提示,不再只吞异常 - chat_screen 接入统一发送服务,失败提示真实原因 - account-service 新增 GET /api/directory(登录用户可读启用员工,用于添加同事) - add_friend_screen 增加可编辑验证消息(视觉规范 4.2) - new_friends_screen 五态 + 窄屏(<360)按钮组换行不挤压姓名(视觉规范 5.3) Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -203,6 +203,11 @@ function readBody(req) {
|
||||
});
|
||||
}
|
||||
|
||||
/** 从已解析的 URL 里取查询参数(原样返回,未解码处理由调用方负责) */
|
||||
function urlParam(req, key) {
|
||||
return req.urlObj ? (req.urlObj.searchParams.get(key) || '') : '';
|
||||
}
|
||||
|
||||
function checkAdmin(req, res) {
|
||||
const t = req.headers['admin-token'] || '';
|
||||
const a = Buffer.from(String(t));
|
||||
@@ -221,6 +226,27 @@ function publicView(emp, staffNo) {
|
||||
const routes = {
|
||||
'GET /api/health': async (req, res) => ok(res, { status: 'up', employees: Object.keys(employees).length }),
|
||||
|
||||
// 员工目录:登录用户可读的启用员工列表(供客户端「添加同事」搜索)。
|
||||
// 请求头 Authorization: Bearer <OpenIM imToken>(与 /api/rtc_token 一致,用 parse_token 校验身份)。
|
||||
// 只返回启用员工、排除自己,不返回密码/手机号/管理字段。
|
||||
'GET /api/directory': async (req, res) => {
|
||||
const m = /^Bearer\s+(.+)$/.exec(String(req.headers.authorization || ''));
|
||||
if (!m) return fail(res, '未登录或登录已过期', 401);
|
||||
const userID = await parseImToken(m[1]);
|
||||
if (!userID) return fail(res, '登录已过期,请重新登录', 401);
|
||||
const keyword = (urlParam(req, 'keyword') || '').trim();
|
||||
const limit = Math.min(Math.max(parseInt(urlParam(req, 'limit') || '20', 10) || 20, 1), 100);
|
||||
let list = Object.keys(employees)
|
||||
.filter((k) => employees[k].status === 'active' && k !== userID)
|
||||
.map((k) => ({ userID: k, nickname: employees[k].name, department: '', title: '', faceURL: '' }));
|
||||
if (keyword) {
|
||||
const kw = keyword.toLowerCase();
|
||||
list = list.filter((u) => u.userID.toLowerCase().includes(kw) || u.nickname.toLowerCase().includes(kw));
|
||||
}
|
||||
const items = list.slice(0, limit);
|
||||
ok(res, { items, nextCursor: null });
|
||||
},
|
||||
|
||||
'POST /api/login': async (req, res, body) => {
|
||||
const { staffNo, password, platformID } = body;
|
||||
if (!staffNo || !password) return fail(res, '工号和密码不能为空');
|
||||
@@ -334,6 +360,7 @@ const routes = {
|
||||
const server = http.createServer(async (req, res) => {
|
||||
try {
|
||||
const url = new URL(req.url, 'http://localhost');
|
||||
req.urlObj = url;
|
||||
if (req.method === 'OPTIONS') return send(res, 204, {});
|
||||
// 管理页(免登录打开,页面内再输入管理口令)
|
||||
if (req.method === 'GET' && (url.pathname === '/' || url.pathname === '/admin')) {
|
||||
|
||||
Reference in New Issue
Block a user