chore(release): 冻结 v1.1.0 版本
- 同步版本号:VERSION / backend pom / admin-web / miniapp 均升至 1.1.0 - 合并本轮代码审查修复(详见 docs/CODE-REVIEW-2026-09-24.md): - 后端:微信登录真实实现 RealWechatLoginProvider、实名核验真实实现 RealIdentityVerificationProvider(本地强校验 + 可配置第三方 + fail-closed/fail-fast) - 照片直传落库端点 POST /me/photos/confirm(fileKey 归属校验 + 对象存在性校验后 savePhoto) - 刷新 Token Redis TTL 修正(getExpirationMs()*2 的 4h → refreshExpirationMs 的 14 天) - 管理端 token 改为 HttpOnly Cookie + silent-refresh,移除 localStorage 明文存储与 30+ 处手工注入 - 小程序:红娘接单鉴权头错位修复、serviceType 中文映射、生产域名可配置 - 隔离本地产物(.workbuddy/、*.bundle、*.dump、secrets、application-dev.yml 等已被 .gitignore 覆盖) 未验证项(打标签 ≠ 生产发布): - 后端编译 / 单测未在本环境执行(Maven 慢、Docker DNS 受限) - 管理端 登录→刷新→登出 端到端、小程序真机走查待人工验收 - 生产部署需注入 WECHAT_APP_ID / WECHAT_APP_SECRET、VERIFICATION_API_URL / VERIFICATION_API_KEY 与小程序生产域名
This commit is contained in:
@@ -104,6 +104,14 @@ const MATCHMAKER_SERVICE_STATUS_LABEL = {
|
||||
cancelled: '已取消',
|
||||
}
|
||||
|
||||
// ============ 红娘服务类型(matchmaker 工作台待处理卡片) ============
|
||||
// 取值须与后端 chk_matchmaker_service_type 白名单一致:recommend_partner / review_preference / contact_intention
|
||||
const MATCHMAKER_SERVICE_TYPE_LABEL = {
|
||||
recommend_partner: '推荐对象',
|
||||
review_preference: '择偶指导',
|
||||
contact_intention: '牵线意向',
|
||||
}
|
||||
|
||||
const GROUPS = {
|
||||
maritalStatus: MARITAL,
|
||||
childrenStatus: CHILDREN_STATUS,
|
||||
@@ -145,6 +153,12 @@ function futureDanyangLabel(v) {
|
||||
return v === undefined || v === null || v === '' ? '' : String(v)
|
||||
}
|
||||
|
||||
/** 红娘服务类型 → 中文(未知码回退原值,便于排查) */
|
||||
function serviceTypeLabel(code) {
|
||||
if (code === undefined || code === null || code === '') return ''
|
||||
return MATCHMAKER_SERVICE_TYPE_LABEL[code] !== undefined ? MATCHMAKER_SERVICE_TYPE_LABEL[code] : String(code)
|
||||
}
|
||||
|
||||
// ============ picker 选项数组(value/label 形式,供 wxml range + range-key 使用) ============
|
||||
const ACCEPT_CHILDREN_OPTIONS = [
|
||||
{ value: 'any', label: '不限' },
|
||||
@@ -193,10 +207,12 @@ module.exports = {
|
||||
SMOKING,
|
||||
DRINKING,
|
||||
MATCHMAKER_SERVICE_STATUS_LABEL,
|
||||
MATCHMAKER_SERVICE_TYPE_LABEL,
|
||||
GROUPS,
|
||||
labelOf,
|
||||
yesNoLabel,
|
||||
futureDanyangLabel,
|
||||
serviceTypeLabel,
|
||||
REGION_CODES,
|
||||
regionCodeOf,
|
||||
ACCEPT_CHILDREN_OPTIONS,
|
||||
|
||||
+4
-1
@@ -22,8 +22,11 @@ const isDev = (function() {
|
||||
})();
|
||||
|
||||
module.exports = {
|
||||
// 生产环境后端地址(线上正式域名)。
|
||||
// 部署前必须替换为真实域名,并到小程序后台「开发管理 → 开发设置 → 服务器域名」配置 request 合法域名。
|
||||
// 注意:本文件不得再出现散落的硬编码地址,统一改这里即可。
|
||||
API_BASE_URL: isDev
|
||||
? 'http://localhost:8085/api/v1'
|
||||
: 'https://api.danyang-match.example.com/api/v1',
|
||||
: 'https://api.danyang-match.example.com/api/v1', // TODO(部署): 替换为真实生产域名
|
||||
IS_DEV: isDev,
|
||||
};
|
||||
|
||||
+9
-1
@@ -19,8 +19,16 @@ function isDev(): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
// 生产环境后端地址(线上正式域名)。
|
||||
// 部署前必须替换为真实域名,并到小程序后台「开发管理 → 开发设置 → 服务器域名」配置 request 合法域名。
|
||||
// 如需在构建期注入(CI/打包脚本),可在打包前以全局 __DY_PROD_API_BASE__ 覆盖本默认值。
|
||||
declare const __DY_PROD_API_BASE__: string | undefined;
|
||||
const PROD_API_BASE_URL: string =
|
||||
(typeof __DY_PROD_API_BASE__ !== 'undefined' ? __DY_PROD_API_BASE__ : undefined)
|
||||
|| 'https://api.danyang-match.example.com/api/v1'; // TODO(部署): 替换为真实生产域名
|
||||
|
||||
export const API_BASE_URL: string = isDev()
|
||||
? 'http://localhost:8085/api/v1'
|
||||
: 'https://api.danyang-match.example.com/api/v1';
|
||||
: PROD_API_BASE_URL;
|
||||
|
||||
export const IS_DEV: boolean = isDev();
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "danyang-match-miniapp",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "danyang-match-miniapp",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"devDependencies": {
|
||||
"miniprogram-api-typings": "^5.2.3",
|
||||
"typescript": "^5.3.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "danyang-match-miniapp",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"description": "丹阳公益婚恋服务平台 - 微信小程序端",
|
||||
"scripts": {
|
||||
"dev": "echo 'Use WeChat DevTools to open miniapp/'",
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* 展示待处理服务、工作台数据概览
|
||||
*/
|
||||
const api = require('../../utils/api');
|
||||
const { serviceTypeLabel } = require('../../constants/profileLabels');
|
||||
|
||||
Page({
|
||||
data: {
|
||||
@@ -82,7 +83,9 @@ Page({
|
||||
wx.showLoading({ title: '接单中...' });
|
||||
|
||||
const token = wx.getStorageSync('operatorToken');
|
||||
api.post(`/operator/matchmaker/services/${serviceId}/claim`, {}, {}, { Authorization: `Bearer ${token}` })
|
||||
// 注意:api.post 签名为 (url, data, headers, idempotencyKey),Authorization 必须放第 3 参(headers),
|
||||
// 此前误放到第 4 参(idempotencyKey),导致实际请求不携带任何鉴权头、接单必 401。
|
||||
api.post(`/operator/matchmaker/services/${serviceId}/claim`, {}, { Authorization: `Bearer ${token}` })
|
||||
.then(res => {
|
||||
wx.hideLoading();
|
||||
if (res.success) {
|
||||
@@ -112,6 +115,11 @@ Page({
|
||||
return m + '月' + d + '日';
|
||||
},
|
||||
|
||||
// 红娘服务类型 → 中文(避免枚举码直接上屏,见 profileLabels 铁律)
|
||||
serviceTypeLabel(code) {
|
||||
return serviceTypeLabel(code);
|
||||
},
|
||||
|
||||
onLogout() {
|
||||
wx.showModal({
|
||||
title: '退出登录',
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
>
|
||||
<view class="service-left">
|
||||
<view class="service-header">
|
||||
<view class="service-tag">{{item.serviceType === 'recommend_partner' ? '推荐对象' : item.serviceType}}</view>
|
||||
<view class="service-tag">{{serviceTypeLabel(item.serviceType)}}</view>
|
||||
<view class="service-time">{{formatTime(item.createdAt)}}</view>
|
||||
</view>
|
||||
<view class="service-desc">{{item.description}}</view>
|
||||
|
||||
@@ -110,9 +110,9 @@ Page({
|
||||
async _uploadPhoto(filePath, photoType) {
|
||||
appStore.showLoading('上传中…');
|
||||
try {
|
||||
// 1. 获取上传凭证(EXIF 由服务端清理)
|
||||
// 1. 获取上传凭证(EXIF 由服务端清理);带上 photoType 使 fileKey 路径与类型一致
|
||||
const fileName = `photo_${Date.now()}.jpg`;
|
||||
const tokenRes = await profileService.requestUploadToken({ fileName, contentType: 'image/jpeg' });
|
||||
const tokenRes = await profileService.requestUploadToken({ fileName, contentType: 'image/jpeg', photoType });
|
||||
|
||||
// 2. 上传到对象存储
|
||||
await new Promise((resolve, reject) => {
|
||||
@@ -129,9 +129,12 @@ Page({
|
||||
});
|
||||
});
|
||||
|
||||
// 3. 若上传的是头像区,或当前无主头像,则设为主头像
|
||||
// 3. 直传成功后必须调 confirm 落库,否则对象成孤儿、照片不展示(修复"照片直传不落库")
|
||||
const confirmRes = await profileService.confirmPhoto({ fileKey: tokenRes.fileKey, photoType });
|
||||
|
||||
// 4. 若上传的是头像区,或当前无主头像,则设为主头像(用真实 photoId)
|
||||
if (photoType === 'avatar' || this.data.avatarPhotos.length === 0) {
|
||||
await profileService.setMainPhoto(tokenRes.photoId);
|
||||
await profileService.setMainPhoto(confirmRes.photoId);
|
||||
}
|
||||
|
||||
appStore.hideLoading();
|
||||
|
||||
@@ -80,6 +80,18 @@ function requestUploadToken(payload) {
|
||||
return api.post('/me/photos/upload-token', payload, undefined, payload.fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认直传照片落库(对应 REST §29 补:POST /me/photos/confirm)
|
||||
* 客户端直传对象存储成功后必须调用,否则对象成孤儿、照片不展示、签名访问也过不了归属校验。
|
||||
* @param {{ fileKey: string, photoType: string }} payload
|
||||
* @returns {Promise<{ photoId: string, photoType: string }>}
|
||||
*/
|
||||
function confirmPhoto(payload) {
|
||||
// 以 fileKey 作幂等指纹:同一文件重复确认只落库一次(直传凭证可重试)
|
||||
const idempotencyKey = api.idempotencyKey('photo-confirm', payload.fileKey);
|
||||
return api.post('/me/photos/confirm', payload, undefined, idempotencyKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取隐私设置(API_INVENTORY 行 23)
|
||||
* @returns {Promise<import('../types').PrivacySettings>}
|
||||
@@ -178,6 +190,7 @@ module.exports = {
|
||||
deletePhoto,
|
||||
setMainPhoto,
|
||||
requestUploadToken,
|
||||
confirmPhoto,
|
||||
getPrivacySettings,
|
||||
updatePrivacySettings,
|
||||
getReviewStatus,
|
||||
|
||||
Reference in New Issue
Block a user