2026-09-06-ajyy-feedback-platform.md 27 KB

安建医院纪检扫码反馈系统 Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 交付可验收的纪检扫码反馈平台:微信 H5 提交反馈/建言 + Web 管理端台账办理与配置 + FastAPI 后端(含角色菜单授权)。

Architecture: 单仓三端——backend(FastAPI)提供 /api/public/* 与 /api/web/*;mobile-wx(uni-app→H5)只调公开接口;web-admin(Vue3+Element Plus)JWT 登录后按 sys_role_menu 动态菜单。MySQL 库 ajyy,MinIO 独立 bucket ajyy。

Tech Stack: Python 3.9+ / FastAPI 0.109 / SQLAlchemy 2 / PyMySQL / bcrypt / python-jose / minio;Vue 3.5 + Vite + Element Plus + Pinia + vue-router(hash) + Axios;uni-app Vue3 H5。

Spec: docs/superpowers/specs/2026-09-06-ajyy-feedback-platform-design.md

Global Constraints

  • 统一响应:{ code: 200, message, data, timestamp }(毫秒时间戳)
  • 单院、无 tenant_no;软删除 del_flag(0 正常 / 1 删除)
  • 主键 id:UUID 字符串;附件 URL 列表:JSON 数组
  • 反馈状态:待处理 | 处理中 | 已办结;建言状态:待查阅 | 已采纳 | 已回复
  • 品牌色移动端 #a51f23;视觉/字段对照 原型图/index.html、原型图/admin.html
  • 本期不上小程序;不做公开限流/脱敏/RSA/API 加密
  • 参考实现可看 ../shanxia2.0/backend 等,但禁止拷贝 CRM/报销等业务模块
  • 测试默认:pytest + FastAPI TestClient;本地可用 SQLite(sqlite+pysqlite:///:memory:)跑单测,联调再用 MySQL ajyy

File Structure (锁死职责)

backend/
  requirements.txt
  .env.example
  pytest.ini
  app/
    main.py                 # 挂载路由、CORS、启动种子
    core/
      config.py             # Settings
      database.py           # engine, SessionLocal, get_db
      response.py           # success_response / error_response
      security.py           # hash/verify password, create/decode JWT
      deps.py               # get_current_user, require_menu(*codes)
      storage.py            # MinIO 上传(STORAGE_TYPE=local|minio)
    models/
      __init__.py
      system.py             # SysUser, SysRole, SysMenu, SysRoleMenu, OperLog
      business.py           # Feedback, Suggestion, HotlineConfig, QrcodeConfig, Attachment
    schemas/
      auth.py
      system.py
      business.py
      common.py             # PageResult
    crud/
      user.py, role.py, menu.py, feedback.py, suggestion.py, config.py, attachment.py, oper_log.py
    services/
      auth_service.py
      feedback_service.py
      suggestion_service.py
      config_service.py
      dashboard_service.py
      seed.py               # 幂等种子
    api/
      public.py             # /api/public/*
      auth.py               # /api/web/auth/*
      admin.py              # /api/web/admin/*
  tests/
    conftest.py
    test_health.py
    test_auth.py
    test_public_feedback.py
    test_admin_feedback.py
    test_role_menus.py
  docs/sql/
    001_init.sql            # 可选:MySQL DDL 对照

web-admin/
  package.json
  vite.config.ts
  .env.development
  index.html
  src/
    main.ts
    App.vue
    styles/index.css
    utils/http.ts
    api/auth.ts, admin.ts
    stores/user.ts
    router/index.ts
    layouts/AdminLayout.vue
    views/login/index.vue
    views/dashboard/index.vue
    views/feedback/index.vue
    views/suggestion/index.vue
    views/hotline/index.vue
    views/qrcode/index.vue
    views/user/index.vue
    views/role/index.vue
    views/audit/index.vue

mobile-wx/
  pages.json                # 注册全部 H5 页
  config/api.js
  utils/request.js
  utils/upload.js
  api/public.js
  pages/home/index.vue
  pages/notice/index.vue
  pages/feedback/index.vue
  pages/suggest/index.vue
  pages/hotline/index.vue
  (分享二维码可用 home 弹层,不必单独路由)
  static/                   # logo 等,可从 原型图/assets 拷贝

Task 1: Backend 骨架 + 健康检查

Files:

  • Create: backend/requirements.txt
  • Create: backend/pytest.ini
  • Create: backend/app/__init__.py
  • Create: backend/app/main.py
  • Create: backend/app/core/config.py
  • Create: backend/app/core/response.py
  • Create: backend/tests/conftest.py
  • Create: backend/tests/test_health.py

Interfaces:

  • Produces: success_response(data=None, message="success") -> dict;Settings via get_settings();GET /api/health → {code:200, data:{status:"ok"}}

  • [ ] Step 1: Write failing health test

# backend/tests/test_health.py
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_health():
    r = client.get("/api/health")
    assert r.status_code == 200
    body = r.json()
    assert body["code"] == 200
    assert body["data"]["status"] == "ok"
    assert "timestamp" in body
  • Step 2: Run test — expect fail (app missing)

Run: cd backend && python -m pytest tests/test_health.py -v
Expected: import/collection error or 404

  • Step 3: Implement minimal app

requirements.txt(精简):

fastapi==0.109.0
uvicorn[standard]==0.27.0
sqlalchemy==2.0.25
pymysql==1.1.0
pydantic==2.5.3
pydantic-settings==2.1.0
python-jose[cryptography]==3.3.0
bcrypt==4.0.1
python-multipart==0.0.6
python-dotenv==1.0.0
minio==7.2.20
httpx==0.26.0
pytest==8.0.0

app/core/response.py:照搬 shanxia 的 success_response / error_response(含毫秒 timestamp)。

app/core/config.py:

from functools import lru_cache
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    APP_NAME: str = "ajyy"
    API_V1_PREFIX: str = "/api"
    DATABASE_URL: str = "sqlite+pysqlite:///:memory:"
    SECRET_KEY: str = "change-me-in-production"
    ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 12
    ALGORITHM: str = "HS256"
    INIT_ADMIN_USERNAME: str = "admin"
    INIT_ADMIN_PASSWORD: str = "Admin@123"
    STORAGE_TYPE: str = "local"  # local | minio
    LOCAL_STORAGE_DIR: str = "./uploads"
    MINIO_ENDPOINT: str = "127.0.0.1:9000"
    MINIO_ACCESS_KEY: str = ""
    MINIO_SECRET_KEY: str = ""
    MINIO_BUCKET: str = "ajyy"
    MINIO_SECURE: bool = False
    MINIO_BASE_URL: str = ""
    CORS_ORIGINS: str = "*"

    class Config:
        env_file = ".env"
        extra = "ignore"

@lru_cache
def get_settings() -> Settings:
    return Settings()

app/main.py:创建 FastAPI,CORS,GET /api/health 返回 success_response({"status": "ok"})。

  • Step 4: Install deps + pass test

Run: cd backend && pip install -r requirements.txt && python -m pytest tests/test_health.py -v
Expected: PASS

  • Step 5: Commit
git add backend
git commit -m "feat(backend): scaffold FastAPI app with health check"

Task 2: 数据库模型 + 建表 + 幂等种子

Files:

  • Create: backend/app/core/database.py
  • Create: backend/app/models/system.py
  • Create: backend/app/models/business.py
  • Create: backend/app/models/__init__.py
  • Create: backend/app/services/seed.py
  • Modify: backend/app/main.py(startup 建表+种子)
  • Modify: backend/tests/conftest.py
  • Create: backend/tests/test_seed.py
  • Create: backend/docs/sql/001_init.sql(MySQL 对照 DDL,可选但推荐)

Interfaces:

  • Produces: ORM 模型名见上;init_db() 建表;seed_if_empty(db) 幂等写入 3 角色、菜单树、admin 用户、热线/二维码各 1 行
  • Menu codes(固定):dashboard,feedback,suggestion,hotline,qrcode,user,role,audit;按钮:feedback:handle,suggestion:handle,hotline:write,qrcode:write,user:manage,role:grant

  • [ ] Step 1: Write seed test

# backend/tests/test_seed.py
def test_seed_creates_admin_and_roles(db_session):
    from app.services.seed import seed_if_empty
    from app.models.system import SysUser, SysRole, SysMenu, SysRoleMenu
    seed_if_empty(db_session)
    seed_if_empty(db_session)  # idempotent
    assert db_session.query(SysRole).count() == 3
    assert db_session.query(SysUser).filter_by(username="admin").count() == 1
    assert db_session.query(SysMenu).count() >= 8
    assert db_session.query(SysRoleMenu).count() >= 1
  • Step 2: Run — expect fail

Run: cd backend && python -m pytest tests/test_seed.py -v
Expected: FAIL import/missing

  • Step 3: Implement models + database + seed

database.py:create_engine(settings.DATABASE_URL, ...);SQLite 时 connect_args={"check_same_thread": False};SessionLocal;get_db;Base = declarative_base();init_db() → Base.metadata.create_all。

模型字段(必须):

SysUser: id, username, password_hash, display_name, role_id, status(0启用/1停用), del_flag, create_time, update_time, create_by, update_by

SysRole: id, role_code, role_name, remark, del_flag, ...

SysMenu: id, parent_id, menu_code, menu_name, path, menu_type(M目录/C菜单/F按钮), sort, visible, del_flag, ...

SysRoleMenu: id, role_id, menu_id

Feedback: id, biz_no, fb_type(投诉|表扬), target_name, target_dept, target_post, content, reporter_name, reporter_phone, attachments(JSON), status, handle_remark, del_flag, timestamps

Suggestion: id, biz_no, content, contact_name, contact_unit, contact_post, contact_phone, attachments(JSON), status, handle_remark, del_flag, timestamps

HotlineConfig: id, phone, email, address, work_time, ...

QrcodeConfig: id, land_url, title, image_url, ...

Attachment: id, file_name, object_key, url, size, biz_type, biz_id, ...

OperLog: id, user_id, username, module, action, detail, ip, create_time

种子授权矩阵:

role_code 菜单/按钮
admin 全部
handler dashboard, feedback, suggestion, feedback:handle, suggestion:handle
viewer dashboard, feedback, suggestion(无 handle)

密码用 bcrypt hash(passlib 或直接 bcrypt.hashpw)。

  • Step 4: Pass tests

Run: cd backend && python -m pytest tests/test_seed.py -v
Expected: PASS

  • Step 5: Commit
git add backend
git commit -m "feat(backend): add models, init_db, and idempotent seed data"

Task 3: 登录鉴权 JWT + current-user + menus

Files:

  • Create: backend/app/core/security.py
  • Create: backend/app/core/deps.py
  • Create: backend/app/schemas/auth.py
  • Create: backend/app/crud/user.py
  • Create: backend/app/crud/menu.py
  • Create: backend/app/services/auth_service.py
  • Create: backend/app/api/auth.py
  • Modify: backend/app/main.py(include router)
  • Create: backend/tests/test_auth.py
  • Modify: backend/tests/conftest.py(提供已种子的 client)

Interfaces:

  • Produces:

    • POST /api/web/auth/login body {username, password} → {token, tokenType:"Bearer"}
    • GET /api/web/auth/current-user Bearer → {id, username, displayName, roleCode, roleName}
    • GET /api/web/auth/menus → 树形菜单列表(仅 menu_type in M/C,按 sort)
    • POST /api/web/auth/logout → success
    • require_menu(*codes):当前用户角色若无任一 code → 403
    • get_current_user:无效/过期 token → 401
  • [ ] Step 1: Write auth tests

def test_login_and_menus(client):
    r = client.post("/api/web/auth/login", json={"username": "admin", "password": "Admin@123"})
    assert r.status_code == 200
    token = r.json()["data"]["token"]
    me = client.get("/api/web/auth/current-user", headers={"Authorization": f"Bearer {token}"})
    assert me.json()["data"]["roleCode"] == "admin"
    menus = client.get("/api/web/auth/menus", headers={"Authorization": f"Bearer {token}"})
    codes = {m["menuCode"] for m in _flatten(menus.json()["data"])}
    assert "dashboard" in codes
    assert "role" in codes

def test_login_wrong_password(client):
    r = client.post("/api/web/auth/login", json={"username": "admin", "password": "bad"})
    assert r.status_code == 200
    assert r.json()["code"] == 401  # 或 HTTP 401;全项目统一一种,推荐 HTTP 401

(选定:HTTP 状态码 401/403,body 仍可用 error_response。)

  • [ ] Step 2: Run — expect fail

  • [ ] Step 3: Implement security + auth routes

create_access_token(sub: str, extra: dict) -> str;decode_token;OAuth2 bearer。
登录失败写 oper_log 可选;成功登录写 oper_log(module=auth, action=login)。

JSON 字段对外 camelCase(Pydantic alias_generator 或手动 dict)。

  • Step 4: Pass tests + commit
git add backend
git commit -m "feat(backend): add JWT login, current-user, and menus API"

Task 4: 附件存储 + 公开上传

Files:

  • Create: backend/app/core/storage.py
  • Create: backend/app/crud/attachment.py
  • Create: backend/app/api/public.py(先只 upload + 空路由壳)
  • Create: backend/tests/test_upload.py
  • Create: backend/.env.example

Interfaces:

  • Produces: storage.upload_file(content: bytes, filename: str, folder: str) -> dict 含 url, objectKey
  • POST /api/public/attachment/upload multipart field file,可选 folder → {id, url, fileName, size}
  • STORAGE_TYPE=local 时落到 LOCAL_STORAGE_DIR,URL 形如 /uploads/...;并用 StaticFiles 挂载便于本地预览

  • [ ] Step 1: Write upload test (local storage)

def test_public_upload(client, tmp_path, monkeypatch):
    monkeypatch.setenv("STORAGE_TYPE", "local")
    monkeypatch.setenv("LOCAL_STORAGE_DIR", str(tmp_path))
    # 重新 load settings / app 若需要
    files = {"file": ("a.png", b"fake-png-bytes", "image/png")}
    r = client.post("/api/public/attachment/upload", files=files)
    assert r.status_code == 200
    data = r.json()["data"]
    assert data["url"]
    assert data["id"]
  • Step 2–4: Implement MinIO 分支(endpoint/bucket 来自 settings;bucket 不存在则创建)+ local;pass test;commit
git commit -m "feat(backend): add local/minio storage and public upload API"

Task 5: 公开反馈 / 建言 / 热线 / 二维码

Files:

  • Create: backend/app/schemas/business.py
  • Create: backend/app/crud/feedback.py
  • Create: backend/app/crud/suggestion.py
  • Create: backend/app/crud/config.py
  • Create: backend/app/services/feedback_service.py
  • Create: backend/app/services/suggestion_service.py
  • Create: backend/app/services/config_service.py
  • Modify: backend/app/api/public.py
  • Create: backend/tests/test_public_feedback.py

Interfaces:

  • GET /api/public/hotline → 热线对象
  • GET /api/public/qrcode → 二维码配置
  • POST /api/public/feedback body: {fbType, targetName?, targetDept?, targetPost?, content, reporterName?, reporterPhone?, attachmentUrls?: string[]} → {id, bizNo};默认 status=待处理;biz_no=FK+YYYYMMDD+3 位日序号
  • POST /api/public/suggestion 类似;biz_no 前缀 JY;status=待查阅
  • content 空 → 400

  • [ ] Step 1: Tests

def test_submit_feedback(client):
    r = client.post("/api/public/feedback", json={
        "fbType": "投诉", "content": "测试投诉内容"
    })
    assert r.status_code == 200
    assert r.json()["data"]["bizNo"].startswith("FK")

def test_feedback_requires_content(client):
    r = client.post("/api/public/feedback", json={"fbType": "投诉", "content": ""})
    assert r.status_code == 400

def test_get_hotline(client):
    assert client.get("/api/public/hotline").json()["code"] == 200
  • Step 2–4: Implement + pass + commit
git commit -m "feat(backend): add public feedback, suggestion, and config APIs"

Task 6: 管理端台账、办理、工作台、配置写入

Files:

  • Create: backend/app/api/admin.py
  • Create: backend/app/services/dashboard_service.py
  • Create: backend/app/crud/oper_log.py
  • Create: backend/tests/test_admin_feedback.py

Interfaces:

  • GET /api/web/admin/dashboard → {feedbackTotal, complaintPending, suggestionTotal, weekNew, recentFeedback:[]}
  • GET /api/web/admin/feedbacks?status=&fbType=&keyword=&page=1&pageSize=10 → {list, total, page, pageSize}
  • GET /api/web/admin/feedbacks/{id}
  • PUT /api/web/admin/feedbacks/{id}/status body {status, handleRemark?} — 需 feedback:handle;非法状态迁移 → 400;写 oper_log
  • 建言对称:/suggestions,权限 suggestion:handle
  • GET/PUT /api/web/admin/hotline — PUT 需 hotline:write
  • GET/PUT /api/web/admin/qrcode — PUT 需 qrcode:write
  • POST /api/web/admin/attachment/upload — 需登录(任意已登录角色)

状态迁移允许:

  • 反馈:待处理→处理中|已办结;处理中→已办结;同状态仅更新备注允许
  • 建言:待查阅→已采纳|已回复;已采纳/已回复之间互转允许

  • [ ] Step 1: Tests(viewer 无办理权限;handler 可办理;admin 可改配置)

用种子额外创建 handler/viewer 测试用户,或在 test 里直接插用户。

def test_viewer_cannot_handle(client, viewer_headers):
    # 先造一条反馈
    fb = client.post("/api/public/feedback", json={"fbType": "投诉", "content": "x"*4}).json()["data"]
    r = client.put(f"/api/web/admin/feedbacks/{fb['id']}/status",
                   headers=viewer_headers,
                   json={"status": "处理中"})
    assert r.status_code == 403
  • Step 2–4: Implement + pass + commit
git commit -m "feat(backend): add admin dashboard, ledger, handle, and config APIs"

Task 7: 用户管理 + 角色授权 + 审计列表

Files:

  • Create: backend/app/schemas/system.py
  • Create: backend/app/crud/role.py
  • Modify: backend/app/api/admin.py
  • Create: backend/tests/test_role_menus.py

Interfaces:

  • GET /api/web/admin/users — 需 user:manage
  • POST /api/web/admin/users body {username, password, displayName, roleId}
  • PUT /api/web/admin/users/{id} body {displayName?, roleId?, status?}
  • PUT /api/web/admin/users/{id}/reset-password body {password}
  • GET /api/web/admin/roles
  • GET /api/web/admin/menus/tree — 全量菜单树(授权页用)
  • GET /api/web/admin/roles/{id}/menus → menuIds: string[]
  • PUT /api/web/admin/roles/{id}/menus body {menuIds: string[]} — 需 role:grant;全量替换 sys_role_menu
  • GET /api/web/admin/oper-logs?page&pageSize — 需登录且菜单 audit(admin 种子有)

  • [ ] Step 1: Test role grant changes menus for user

def test_role_grant_updates_menus(client, admin_headers, db_session):
    roles = client.get("/api/web/admin/roles", headers=admin_headers).json()["data"]
    viewer = next(r for r in roles if r["roleCode"] == "viewer")
    # 去掉 suggestion 菜单后再查 viewer 用户 menus…
  • Step 2–4: Implement + pass + commit
git commit -m "feat(backend): add user admin, role menu grant, and oper-log list"

Task 8: web-admin 工程骨架 + 登录 + 动态菜单布局

Files:

  • Create: web-admin/ 全套 Vite Vue3 TS 工程(可用 npm create vite@latest web-admin -- --template vue-ts 后加依赖)
  • Create: 见 File Structure 中 web-admin/src/** 登录/布局/路由/store/http

Interfaces:

  • Consumes: Task 3 auth APIs
  • Produces: 可 pnpm/npm run dev;登录后按 menus 渲染侧栏;无 token 跳 /login

  • [ ] Step 1: Scaffold

cd /Users/wuzilong/work/00code/ajyy
npm create vite@latest web-admin -- --template vue-ts
cd web-admin && npm i && npm i element-plus vue-router@4 pinia axios @element-plus/icons-vue

.env.development:

VITE_API_BASE_URL=/api
VITE_API_PROXY_TARGET=http://127.0.0.1:8000

vite.config.ts:proxy /api → VITE_API_PROXY_TARGET;hash history。

  • Step 2: http + auth store

utils/http.ts:axios 实例,请求头 Authorization: Bearer ${token};code!==200 时 ElMessage;401 清 token 跳登录。

stores/user.ts:token, user, menus, login, fetchProfile, logout;hasPerm(code: string) 递归 menus + 按钮(登录后可另存 permCodes: string[]——建议 current-user 或 menus 接口同时返回按钮 codes;实现约定:GET /menus 返回含按钮的扁平 permCodes 放在响应 data.permCodes,树在 data.tree)。

若 Task 3 的 menus 仅返回树,则在本 Task 回头小改后端:

{ "tree": [...], "permCodes": ["feedback:handle", ...] }

并补一条后端测试断言 permCodes。

  • Step 3: Login page + AdminLayout + router guard

路由表:静态 /login;业务路由 meta.menuCode 与菜单 path 对齐:

path menuCode component
/dashboard dashboard dashboard
/feedback feedback feedback
/suggestion suggestion suggestion
/hotline hotline hotline
/qrcode qrcode qrcode
/user user user
/role role role
/audit audit audit

守卫:无 token → login;有 token 无对应 menuCode → /dashboard 或 403 页。

侧栏主色可用纪检红接近色(管理端不必强制 #a51f23,但 logo/标题用「安徽建工集团医院纪检室」)。

  • Step 4: Manual verify

Run backend: uvicorn app.main:app --reload --port 8000
Run admin: npm run dev
登录 admin / Admin@123,侧栏应出现全部菜单。

  • Step 5: Commit
git add web-admin
git commit -m "feat(web-admin): scaffold app with login and dynamic menus"

Task 9: web-admin 业务页(工作台/反馈/建言/热线/二维码)

Files:

  • Create/Modify: web-admin/src/views/dashboard/index.vue
  • Create/Modify: web-admin/src/views/feedback/index.vue
  • Create/Modify: web-admin/src/views/suggestion/index.vue
  • Create/Modify: web-admin/src/views/hotline/index.vue
  • Create/Modify: web-admin/src/views/qrcode/index.vue
  • Create/Modify: web-admin/src/api/admin.ts

Interfaces: Consumes Task 6 APIs。字段与交互对照 原型图/admin.html。

  • [ ] Step 1: dashboard — 四统计卡片 + 最近反馈表(编号、类型、摘要、时间、状态)

  • [ ] Step 2: feedback list — 状态下拉、类型、关键词;表格;详情 Drawer;办理区:状态下拉+备注+保存;无 feedback:handle 时隐藏保存

  • [ ] Step 3: suggestion — 对称实现

  • [ ] Step 4: hotline + qrcode forms — 保存调用 PUT;二维码支持上传自定义图(admin upload)+ 预览;无写权限则表单 disabled

  • [ ] Step 5: Manual verify with handler/viewer accounts(Task 7 可先用 SQL/接口创建)+ Commit

git commit -m "feat(web-admin): add dashboard, ledger, and config pages"

Task 10: web-admin 用户 / 角色授权 / 审计

Files:

  • Create: web-admin/src/views/user/index.vue
  • Create: web-admin/src/views/role/index.vue
  • Create: web-admin/src/views/audit/index.vue

  • [ ] Step 1: 用户列表 — 新建、启停、重置密码、改角色(Element Plus Table + Dialog)

  • [ ] Step 2: 角色授权页 — 左侧角色列表,右侧 el-tree show-checkbox 绑定全量菜单树;保存 PUT .../menus

  • [ ] Step 3: 审计列表 — 简单分页表

  • [ ] Step 4: Verify — 去掉 viewer 的 suggestion 菜单后,用 viewer 登录侧栏不再出现建言;恢复后出现

  • [ ] Step 5: Commit

git commit -m "feat(web-admin): add user admin, role grant, and audit pages"

Task 11: mobile-wx H5 页面 + 公开 API 对接

Files:

  • Modify: mobile-wx/pages.json
  • Modify: mobile-wx/App.vue(全局样式变量 --primary:#a51f23)
  • Create: mobile-wx/config/api.js
  • Create: mobile-wx/utils/request.js
  • Create: mobile-wx/utils/upload.js
  • Create: mobile-wx/api/public.js
  • Create: mobile-wx/pages/home/index.vue
  • Create: mobile-wx/pages/notice/index.vue
  • Create: mobile-wx/pages/feedback/index.vue
  • Create: mobile-wx/pages/suggest/index.vue
  • Create: mobile-wx/pages/hotline/index.vue
  • Remove or repurpose: mobile-wx/pages/index/index.vue

Interfaces: Consumes Task 4–5 public APIs。UI 对照 原型图/index.html(布局/文案/倒计时 5 秒)。

  • Step 1: request/upload/config
// config/api.js
export const BASE_URL = 'http://127.0.0.1:8000/api' // H5 开发可改;真机用局域网 IP

request.js:uni.request 封装,解析 code===200。
upload.js:uni.chooseImage / uni.chooseFile + uni.uploadFile → /public/attachment/upload。

  • [ ] Step 2: pages.json 注册五页;首页为 pages/home/index;navigationBar 红色

  • [ ] Step 3: 实现各页

  • home:四大入口;分享弹层读 /public/qrcode(有 imageUrl 用图,否则用 landUrl + 第三方或 canvas 生成二维码——可用纯图片配置优先,避免强依赖库;若无自定义图则展示 URL 文本 + 提示「请使用后台上传二维码图」或引入轻量 uQRCode)

  • notice:须知 + 5s countdown 后可点「已阅读并继续」

  • feedback:类型切换、被反馈人、内容、附件、匿名联系方式、提交成功 Toast/回首页

  • suggest:对称

  • hotline:展示配置,tel: 拨打

  • [ ] Step 4: HBuilderX 运行到浏览器 / 微信开发者工具 H5,走通提交 → 管理端可见

  • [ ] Step 5: Commit

git add mobile-wx
git commit -m "feat(mobile-wx): add H5 feedback portal pages wired to public API"

Task 12: 环境接通文档 + 联调验收清单

Files:

  • Create: backend/.env.example(若未完备则补全)
  • Create: docs/deploy-test.md
  • Create: docs/acceptance-checklist.md

  • [ ] Step 1: 写测试环境说明

docs/deploy-test.md 内容须含:

  1. 创建 MySQL 库 ajyy(utf8mb4)
  2. DATABASE_URL=mysql+pymysql://USER:PASS@HOST:3306/ajyy
  3. MinIO:STORAGE_TYPE=minio,bucket ajyy,填写与 shanxia 测试环境相同 endpoint/密钥
  4. 启动:uvicorn app.main:app --host 0.0.0.0 --port 8000
  5. web-admin build / nginx 反代 /api
  6. mobile-wx H5 打包部署路径与二维码落地 URL 配置到 qrcode_config.land_url
  • [ ] Step 2: 验收清单 — 逐项对应建设清单 1–17(功能),每项可勾选

  • [ ] Step 3: 在测试环境实际跑通最小路径(若凭证可用):提交一条反馈 + admin 办理 + 改热线在 H5 可见

  • [ ] Step 4: Commit

git add docs backend/.env.example
git commit -m "docs: add test deploy guide and acceptance checklist"

Spec coverage self-check

Spec 项 Task
仓库三端结构 1, 8, 11
MySQL ajyy / MinIO bucket 2, 4, 12
sys_user/role/menu/role_menu 2, 3, 7
feedback/suggestion/hotline/qrcode/attachment/oper_log 2, 5, 6, 7
公开 API 4, 5
JWT 登录 / menus / 角色授权页 3, 7, 8, 10
管理台账办理/工作台/配置 6, 9
用户管理 7, 10
mobile 6 页能力 11
不做限流脱敏/小程序 全局约束 + 未列入任务
验收/部署说明 12

Placeholder / consistency notes (已在正文写死)

  • 对外 JSON:camelCase
  • 鉴权失败:HTTP 401/403
  • menus 响应:{ tree, permCodes }(Task 8 若需则回改 Task 3)
  • 附件字段:业务表 JSON 数组;上传返回 id/url
  • 默认管理员:admin / Admin@123(仅 .env / 种子,上线必改)