Files
danyang-match/miniapp/pages/mine/mine.wxml
T
WorkBuddy 72b4e9b846 fix(ops-backend/service-station): close Phase 19 KEEP-VERIFY — 25 defects (unreachable announcements + station write privilege escalation; banner 500 + soft-delete leak; content publish dead-end; program approval dead-end/draft leak/zero counts/missing withdraw; zero-coverage test schema) ; add V042 announcement chain + 34 guard tests; full regression 602 green
Backend
- Service Station: expose the 「临时公告」 chain end-to-end (V042 table/service existed
  but had ZERO endpoints -> both admin and miniapp could never reach it): 5 operator
  endpoints (list/create/update/publish/archive) + 1 user-facing read endpoint.
- Fix privilege escalation: create/update were guarded only by the class-level
  service_location.view, making V015's service_location.manage a never-used dead
  permission -> read-only roles could edit stations. Permissions moved to method level.
- Add ServiceLocationOperatorController to OperatorEndpointPermissionCoverageTest
  (it was NOT scanned, which is why the escalation stayed invisible).
- Add create/update validation (address NOT NULL + status CHECK only blew up on PG).
- Banner: validation failures threw bare RuntimeException -> 500; now VALIDATION_ERROR
  -> 400. Add deleted_at IS NULL to both home queries (soft-deleted banners stayed live).
- Content: operator list/detail split (admin «publish» was a dead end because it reused
  the published-only public API); category deactivate/reactivate; article body on edit.
- Service Program: operator member list now includes pending (approval reachable);
  active-only public visibility (draft leak); memberCount/pendingCount aggregation;
  withdraw/removeMember; reuse row on re-apply (UNIQUE(program_id,user_id) crash).
- MeController: add the missing GET /me/service-history route.

Schema / guards
- schema-h2.sql: add service_location_announcements (+ chk_announcement_status) and
  content_categories (+ uk_content_category_code, chk_category_status); move both into
  REQUIRED_IN_TEST_SCHEMA. MigrationValidationTest baseline 41 -> 42.
- V11PermissionMatrixContractTest now scans ALL migrations instead of hardcoding V015+V029.
- New: ServiceLocationAnnouncementPhase19IntegrationTest (24),
  ServiceLocationControllerPermissionContractTest (5),
  ServiceLocationAnnouncementConstraintParityTest (5, prod DDL <-> test schema <->
  backend whitelist <-> admin labels).

Frontend
- admin-web: ServiceStationView gains announcement management (statuses, create/edit,
  publish/archive with confirm, expiry); write buttons gated by service_location.manage.
- miniapp: U50 renders the 临时公告 block via new serviceStationService; page no longer
  talks to utils/api directly; announcement failure degrades to [] instead of erroring.
- ui-tests: U37 four-group + U50 announcement contracts; /home assertion retargeted to
  the service layer.

Verification
- Negative verification: 3 injected defects (drop soft-delete filter / drop test-schema
  CHECK / downgrade write endpoint to view) -> 4 named tests fail precisely, no
  collateral failures; restored, NEG-VAL residue = 0.
- Backend full regression: 602 tests, 0 failures, 0 errors, 3 skipped (baseline 420).
- admin-web: vue-tsc --noEmit EXIT=0, vite build EXIT=0. Miniapp: tsc --noEmit EXIT=0.
- ui-tests 14/14 PASS.
2026-09-17 22:21:31 +08:00

80 lines
7.3 KiB
Plaintext

<view class="page app-page reference-page">
<view wx:if="{{!isLoggedIn}}" class="mine-guest">
<view class="guest-brand"><image src="/assets/brand/logo-mark.png" mode="aspectFit" /><text>善缘丹阳</text><text class="guest-subtitle">公益婚恋服务平台</text></view>
<ui-empty illustration="default" title="登录后查看我的服务" description="管理资料、缘分与公益服务记录" actions="{{loginActions}}" bind:action="onLoginAction" />
</view>
<ui-loading wx:elif="{{loadStatus === 'loading' && !profile}}" mode="skeleton" rows="6" />
<block wx:else>
<view class="profile-hero">
<view class="mine-nav"><text>我的</text><image src="/assets/icons/home/service-station.png" mode="aspectFit" bindtap="onGoSettings" /></view>
<view class="profile-main">
<ui-avatar src="{{profile.avatarUrl || ''}}" size="xl" default-text="{{profile.displayName || '善缘用户'}}" verified="{{isRealNameVerified}}" reviewed="{{isProfileApproved}}" />
<view class="profile-copy">
<view class="profile-name-row"><text class="profile-name">{{profile.displayName || '未填写昵称'}}</text><text class="profile-status">{{relationshipText}}</text></view>
<text class="profile-meta">资料完整度 {{profileCompleteness}}%</text>
<view class="profile-progress"><view style="width:{{profileCompleteness}}%"></view></view>
<view class="profile-badges"><ui-status-badge kind="{{isRealNameVerified ? 'success' : 'warning'}}" label="{{isRealNameVerified ? '已实名' : '未实名'}}" /><ui-status-badge kind="{{isProfileApproved ? 'success' : 'warning'}}" label="{{isProfileApproved ? '资料已审核' : '资料待审核'}}" /></view>
</view>
<text class="edit-profile" bindtap="onEditProfile">编辑资料</text>
</view>
</view>
<view class="profile-stats">
<view class="profile-stat"><text class="stat-value">{{profileCompleteness}}%</text><text class="stat-label">资料完整</text></view>
<view class="profile-stat" bindtap="onGoLikers"><text class="stat-value">{{likersCount}}</text><text class="stat-label">喜欢我的</text></view>
<view class="profile-stat" bindtap="onGoFate"><text class="stat-value">{{matchCount}}</text><text class="stat-label">当前缘分</text></view>
<view class="profile-stat"><text class="stat-value stat-value--text">{{reviewStatusText}}</text><text class="stat-label">资料状态</text></view>
</view>
<view class="public-identity-card" bindtap="onEditProfile">
<image src="/assets/brand/logo-mark.png" mode="aspectFit" />
<view><text class="identity-title">我在善缘丹阳</text><text class="identity-desc">用真诚遇见美好,让爱在丹阳绽放</text></view>
<image class="identity-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" />
</view>
<!-- 分组一:我的资料与缘分(U37 四组结构)-->
<ui-section-header title="我的资料与缘分" subtitle="资料、择偶条件与当前缘分" />
<view class="list-group">
<view class="list-row" bindtap="onEditProfile"><text class="list-label">我的资料</text><text class="list-value">{{reviewStatusText}}</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onEditPreference"><text class="list-label">择偶条件</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoFate"><text class="list-label">我的缘分</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<!-- 分组二:公益服务(U49 公益服务中心入口 + 四类服务 + 服务记录)-->
<ui-section-header title="我的公益服务" subtitle="服务进度与预约记录" />
<view class="list-group">
<view class="list-row" bindtap="onGoServiceCenter"><text class="list-label">公益服务中心</text><text class="list-value">咨询 · 预约 · 课堂</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<view class="service-grid">
<view class="service-entry" bindtap="onGoMatchmaker"><view class="service-icon rose"><image src="/assets/icons/home/matchmaker.png" mode="aspectFit" /></view><text>我的红娘</text></view>
<view class="service-entry" bindtap="onGoCounseling"><view class="service-icon blue"><image src="/assets/icons/home/counseling.png" mode="aspectFit" /></view><text>我的咨询</text></view>
<view class="service-entry" bindtap="onGoActivities"><view class="service-icon amber"><image src="/assets/icons/home/activity.png" mode="aspectFit" /></view><text>我的活动</text></view>
<view class="service-entry" bindtap="onGoAppointments"><view class="service-icon green"><image src="/assets/icons/home/service-station.png" mode="aspectFit" /></view><text>我的预约</text></view>
</view>
<view class="list-group">
<view class="list-row" bindtap="onGoServiceHistory"><text class="list-label">公益服务记录</text><text class="list-value">仅本人可见</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<!-- 分组三:安全与隐私 -->
<ui-section-header title="安全与隐私" subtitle="隐私、黑名单与账号" />
<view class="list-group secondary-list">
<view class="list-row" bindtap="onGoPrivacy"><text class="list-label">隐私设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoBlacklist"><text class="list-label">黑名单</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSafety"><text class="list-label">举报与安全</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
<view class="list-row" bindtap="onGoSettings"><text class="list-label">账号设置</text><image class="list-arrow" src="/assets/icons/ui/chevron-right.png" mode="aspectFit" /></view>
</view>
<!-- 分组四:公益信息 -->
<ui-section-header title="公益信息" subtitle="课堂、反诈与服务站" />
<view class="service-grid">
<view class="service-entry" bindtap="onGoClassroom"><view class="service-icon amber"><image src="/assets/icons/home/classroom.png" mode="aspectFit" /></view><text>婚恋课堂</text></view>
<view class="service-entry" bindtap="onGoAntiFraud"><view class="service-icon rose"><image src="/assets/icons/home/anti-fraud.png" mode="aspectFit" /></view><text>婚恋反诈</text></view>
<view class="service-entry" bindtap="onGoStation"><view class="service-icon green"><image src="/assets/icons/home/service-station.png" mode="aspectFit" /></view><text>善缘服务站</text></view>
<view class="service-entry" bindtap="onGoAbout"><view class="service-icon blue"><image src="/assets/brand/logo-mark.png" mode="aspectFit" /></view><text>关于善缘丹阳</text></view>
</view>
<view class="logout-row" bindtap="onLogout">退出登录</view>
<ui-danger-action-dialog visible="{{showLogoutDialog}}" action="{{logoutAction}}" bind:confirm="onConfirmLogout" bind:cancel="onCancelLogout" />
</block>
</view>