diff --git a/app/api/property.js b/app/api/property.js index 128f13b..d2f0ebf 100644 --- a/app/api/property.js +++ b/app/api/property.js @@ -262,3 +262,32 @@ export function bindingWx(data) { export function unbindWx(data) { return request.post('unbindWx', data, { useAdminUrl: true }); } + +// ===== 访客登记模块 ===== + +// 访客登记 - 新增 +export function api_addVisit(data) { + return request.post( + 'autogencode/dcvisitbook/save', + data, + { useAdminUrl: true } + ); +} + +// 访客登记 - 记录列表 +export function api_visitRecordList(params) { + return request.get( + 'autogencode/dcvisitbook/list', + params, + { useAdminUrl: true } + ); +} + +// 访客登记 - 详情/凭证信息 +export function api_visitInfo(id) { + return request.get( + 'autogencode/dcvisitbook/info/' + id, + {}, + { useAdminUrl: true } + ); +} diff --git a/app/api/public.js b/app/api/public.js index 91144d0..ab3cc6c 100644 --- a/app/api/public.js +++ b/app/api/public.js @@ -90,6 +90,15 @@ export function getUserPhone(data){ return request.post('wechat/register/binding/phone',data,{noAuth : true}); } +/** + * 小程序微信绑定已有账号(账号+密码) + * 关闭强制获取手机号时使用,将微信 openId 关联到已有账号 + * @param {Object} data { account, password, key } + */ +export function bindRoutineAccount(data) { + return request.post('wechat/register/binding/account', data, { noAuth: true }); +} + /** * APP微信登录 * @param {Object} data diff --git a/app/config/app.js b/app/config/app.js index 8a9fc51..a83f343 100644 --- a/app/config/app.js +++ b/app/config/app.js @@ -38,5 +38,12 @@ module.exports = { // 缓存时间 0 永久 EXPIRE:0, //分页最多显示条数 - LIMIT: 10 + LIMIT: 10, + /** + * 小程序微信登录:新用户是否强制获取手机号 + * true=开启(弹出微信手机号授权,走 register/binding/phone) + * false=关闭(跳转账号密码页,走 register/binding/account 绑定已有账号) + * 可被后端 wechat/getLogo 返回的 routineLoginRequirePhone 覆盖 + */ + MP_WECHAT_REQUIRE_PHONE: false }; diff --git a/app/libs/login.js b/app/libs/login.js index 66c50ec..3442785 100644 --- a/app/libs/login.js +++ b/app/libs/login.js @@ -8,6 +8,47 @@ import auth from './wechat'; import { LOGIN_STATUS, USER_INFO, EXPIRES_TIME, STATE_R_KEY, BACK_URL} from './../config/cache'; +const LOGIN_PAGE_ROUTE = 'pages/users/login/index'; +const WECHAT_LOGIN_PAGE_ROUTE = 'pages/users/wechat_login/index'; + +function normalizeRoute(route) { + return (route || '').replace(/^\//, ''); +} + +function getPrevPageRoute() { + const pages = getCurrentPages(); + if (pages.length < 2) { + return ''; + } + return normalizeRoute(pages[pages.length - 2].route); +} + +/** + * 登录页 ⇄ 微信登录页切换:上一页已是目标页则返回,避免反复 navigateTo 堆栈 + */ +export function switchLoginPage(url) { + const targetRoute = normalizeRoute((url || '').split('?')[0].replace(/^\//, '')); + const prevRoute = getPrevPageRoute(); + if (prevRoute && prevRoute === targetRoute && url.indexOf('?') === -1) { + uni.navigateBack(); + return; + } + uni.navigateTo({ url }); +} + +/** + * 微信授权后进入账号绑定页(replace 当前页,避免栈里叠多层登录相关页面) + */ +export function openLoginBindPage(authKey) { + const url = `/pages/users/login/index?bindWechat=1&authKey=${encodeURIComponent(authKey)}`; + const prevRoute = getPrevPageRoute(); + if (prevRoute === LOGIN_PAGE_ROUTE) { + uni.redirectTo({ url }); + return; + } + uni.redirectTo({ url }); +} + function prePage(){ let pages = getCurrentPages(); let prePage = pages[pages.length - 1]; @@ -38,46 +79,36 @@ export function _toLogin(push, pathLogin) { pathLogin = '/page/users/login/index' Cache.set('login_back_url',path); } - - // // #ifdef H5 - // if (isWeixin()) { - // let urlData = location.pathname + location.search - // if (urlData.indexOf('?') !== -1) { - // urlData += '&go_longin=1'; - // } else { - // urlData += '?go_longin=1'; - // } - // if (!Cache.has('snsapiKey')) { - // auth.oAuth('snsapi_base', urlData); - // } else { - // if (['/pages/user/index'].indexOf(login_back_url) == -1) { - // uni.navigateTo({ - // url: '/pages/users/wechat_login/index' - // }) - // } - // } - // } else { - // if (['/pages/user/index'].indexOf(login_back_url) == -1) { - // uni.navigateTo({ - // url: '/pages/users/login/index' - // }) - // } - // } - // // #endif - - // if (['pages/user/index','/pages/user/index'].indexOf(login_back_url) == -1) { - // // #ifdef MP - // uni.navigateTo({ - // url: '/pages/users/wechat_login/index' - // }) - // // #endif - // } - + if (['pages/user/index','/pages/user/index'].indexOf(login_back_url) == -1) { + // #ifdef H5 + if (isWeixin()) { + let urlData = location.pathname + location.search + if (urlData.indexOf('?') !== -1) { + urlData += '&go_longin=1'; + } else { + urlData += '?go_longin=1'; + } + if (!Cache.has('snsapiKey')) { + auth.oAuth('snsapi_base', urlData); + } else { + uni.navigateTo({ + url: '/pages/users/wechat_login/index' + }) + } + } else { + uni.navigateTo({ + url: '/pages/users/login/index' + }) + } + // #endif + + // #ifdef MP uni.navigateTo({ - url: '/pages/users/login/index' + url: '/pages/users/wechat_login/index' }) - } + // #endif + } } diff --git a/app/manifest.json b/app/manifest.json index 6e796ed..b1c5a4e 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -2,8 +2,8 @@ "name" : "八方物业", "appid" : "__UNI__B905528", "description" : "八方物业", - "versionName" : "1.0.2", - "versionCode" : 1002, + "versionName" : "1.0.3", + "versionCode" : 1003, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/app/pages.json b/app/pages.json index 19d23ae..d5e16f5 100644 --- a/app/pages.json +++ b/app/pages.json @@ -372,6 +372,34 @@ "navigationStyle": "custom" // #endif } + }, + { + "path": "visitor_register/index", + "style": { + "navigationBarTitleText": "访客登记", + "navigationStyle": "custom" + } + }, + { + "path": "visitor_records/index", + "style": { + "navigationBarTitleText": "访问记录", + "navigationStyle": "custom" + } + }, + { + "path": "visitor_user/index", + "style": { + "navigationBarTitleText": "访客个人中心", + "navigationStyle": "custom" + } + }, + { + "path": "visitor_credential/index", + "style": { + "navigationBarTitleText": "访问凭证", + "navigationStyle": "custom" + } } ] }, diff --git a/app/pages/supply_chain/components/CustomHeader.vue b/app/pages/supply_chain/components/CustomHeader.vue new file mode 100644 index 0000000..5f6351d --- /dev/null +++ b/app/pages/supply_chain/components/CustomHeader.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/app/pages/supply_chain/visitor_credential/index.vue b/app/pages/supply_chain/visitor_credential/index.vue new file mode 100644 index 0000000..f93c235 --- /dev/null +++ b/app/pages/supply_chain/visitor_credential/index.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/app/pages/supply_chain/visitor_records/index.vue b/app/pages/supply_chain/visitor_records/index.vue new file mode 100644 index 0000000..d755c48 --- /dev/null +++ b/app/pages/supply_chain/visitor_records/index.vue @@ -0,0 +1,299 @@ + + + + + diff --git a/app/pages/supply_chain/visitor_register/components/VisitorInput.vue b/app/pages/supply_chain/visitor_register/components/VisitorInput.vue new file mode 100644 index 0000000..d0015ff --- /dev/null +++ b/app/pages/supply_chain/visitor_register/components/VisitorInput.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/app/pages/supply_chain/visitor_register/index.vue b/app/pages/supply_chain/visitor_register/index.vue new file mode 100644 index 0000000..1c276a1 --- /dev/null +++ b/app/pages/supply_chain/visitor_register/index.vue @@ -0,0 +1,514 @@ + + + + + diff --git a/app/pages/supply_chain/visitor_user/index.vue b/app/pages/supply_chain/visitor_user/index.vue new file mode 100644 index 0000000..b39b677 --- /dev/null +++ b/app/pages/supply_chain/visitor_user/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/app/pages/users/login/index.vue b/app/pages/users/login/index.vue index 482e7a2..72d45e9 100644 --- a/app/pages/users/login/index.vue +++ b/app/pages/users/login/index.vue @@ -47,9 +47,12 @@
登录
-
登录
+
{{ bindWechatMode ? '绑定微信登录' : '登录' }}
- +
微信登录
+ + +
请使用已有账号密码完成微信绑定,绑定后可直接微信登录
@@ -80,11 +83,14 @@ validatorDefaultCatch } from "@/utils/dialog"; import { - getLogo, appAuth, appleLogin + getLogo, appAuth, appleLogin, bindRoutineAccount } from "@/api/public"; import { VUE_APP_API_URL } from "@/utils"; + import { + switchLoginPage + } from '@/libs/login'; // #ifdef MP-WEIXIN import { wxGZHAuth @@ -116,7 +122,10 @@ appleLoginStatus: false, // 苹果登录强制绑定手机号码状态 appleUserInfo: null, appleShow: false, // 苹果登录版本必须要求ios13以上的 - pendingGzhCode: '' // 公众号授权未绑定时,随账号密码一并提交 + pendingGzhCode: '', // 公众号授权未绑定时,随账号密码一并提交 + // 小程序:微信绑定已有账号模式(来自 wechat_login 页 register 流程) + bindWechatMode: false, + wechatBindAuthKey: '' }; }, watch:{ @@ -132,7 +141,13 @@ this.getCode(); this.getLogoImage(); }, - onLoad() { + onLoad(options) { + // #ifdef MP-WEIXIN + if (options.bindWechat === '1' && options.authKey) { + this.bindWechatMode = true; + this.wechatBindAuthKey = decodeURIComponent(options.authKey); + } + // #endif let self = this uni.getSystemInfo({ success: function(res) { @@ -445,6 +460,43 @@ if (!that.password) return that.$util.Tips({ title: '请填写密码' }); + + // 小程序微信绑定模式:校验账号密码并将微信 openId 关联到该账号 + // #ifdef MP-WEIXIN + if (that.bindWechatMode) { + if (!/^1(3|4|5|7|8|9|6)\d{9}$/i.test(that.account)) { + return that.$util.Tips({ + title: '请输入正确的手机号码' + }); + } + if (!that.wechatBindAuthKey) { + return that.$util.Tips({ + title: '微信授权已失效,请返回重新授权' + }); + } + uni.showLoading({ title: '绑定中' }); + bindRoutineAccount({ + account: that.account, + password: that.password, + key: that.wechatBindAuthKey + }).then(res => { + uni.hideLoading(); + that.wechatBindAuthKey = ''; + that.bindWechatMode = false; + that.$store.commit('LOGIN', { + token: res.data.token + }); + that.getUserInfo(res.data); + }).catch(e => { + uni.hideLoading(); + that.$util.Tips({ + title: e + }); + }); + return; + } + // #endif + const loginPayload = { account: that.account, password: that.password, @@ -471,9 +523,7 @@ }); }, goWechatLogin() { - uni.navigateTo({ - url: "/pages/users/wechat_login/index" - }); + switchLoginPage('/pages/users/wechat_login/index'); }, getUserInfo(data){ this.$store.commit("SETUID", data.uid); @@ -686,6 +736,15 @@ margin-top: 32rpx; background-color: #07c160; } + + .bind-wechat-tip { + margin-top: 24rpx; + padding: 0 20rpx; + text-align: center; + font-size: 24rpx; + color: #999; + line-height: 1.6; + } .tips { margin: 30rpx; diff --git a/app/pages/users/wechat_login/index.vue b/app/pages/users/wechat_login/index.vue index ad56bab..7701a34 100644 --- a/app/pages/users/wechat_login/index.vue +++ b/app/pages/users/wechat_login/index.vue @@ -23,7 +23,11 @@ - + + + + + @@ -59,6 +63,10 @@ } from '@/api/user.js' import Routine from '@/libs/routine'; import wechat from "@/libs/wechat"; + import { + switchLoginPage, + openLoginBindPage + } from '@/libs/login'; export default { data() { return { @@ -72,7 +80,11 @@ authKey: '', options: '', userInfo: {}, - codeNum: 0 + codeNum: 0, + // 小程序:是否强制获取微信手机号(true=弹手机号授权,false=跳转账号密码绑定) + requirePhone: false, + // 微信授权后待绑定的 key(关闭手机号时缓存,供「绑定已有账号」使用) + pendingBindAuthKey: '' } }, components: { @@ -80,9 +92,11 @@ routinePhone }, onLoad(options) { - // getLogo().then(res => { - // this.logoUrl = res.data.logoUrl - // }) + // #ifdef MP + // 先用本地配置同步初始化,避免 getLogo 未返回前 requirePhone 被误判 + this.requirePhone = this.parseRequirePhoneFlag(this.$config.MP_WECHAT_REQUIRE_PHONE); + this.loadMpLoginConfig(); + // #endif let that = this // #ifdef H5 document.body.addEventListener("focusout", () => { @@ -131,6 +145,60 @@ }, methods: { + // #ifdef MP + /** + * 是否强制获取微信手机号(仅 "1" / true 为开启,其余均为关闭) + */ + parseRequirePhoneFlag(value) { + return value === true || value === 1 || String(value) === '1'; + }, + /** + * 拉取小程序微信登录配置(是否强制获取手机号) + * 后端 system_config.routine_login_require_phone:1=开启,0=关闭(默认) + * 未配置时使用 app/config/app.js 中的 MP_WECHAT_REQUIRE_PHONE + */ + loadMpLoginConfig() { + const localDefault = this.parseRequirePhoneFlag(this.$config.MP_WECHAT_REQUIRE_PHONE); + return getLogo().then(res => { + const cfg = (res && res.data) ? res.data : {}; + if (cfg.logoUrl) { + this.logoUrl = cfg.logoUrl; + } + // 仅当后端明确返回 routineLoginRequirePhone 时才覆盖本地默认 + if (cfg.routineLoginRequirePhone !== undefined && cfg.routineLoginRequirePhone !== null && cfg.routineLoginRequirePhone !== '') { + this.requirePhone = this.parseRequirePhoneFlag(cfg.routineLoginRequirePhone); + } else { + this.requirePhone = localDefault; + } + return this.requirePhone; + }).catch(() => { + this.requirePhone = localDefault; + return this.requirePhone; + }); + }, + /** + * 关闭强制手机号时:跳转账号密码页进行微信绑定 + * 若尚未微信授权,提示先点「微信登录」 + */ + goBindAccountLogin() { + if (!this.pendingBindAuthKey) { + return this.$util.Tips({ + title: '请先点击上方「微信登录」完成授权' + }); + } + openLoginBindPage(this.pendingBindAuthKey); + }, + /** + * 新微信用户且关闭强制手机号:跳转绑定页 + */ + goWechatBindLogin(authKey) { + this.pendingBindAuthKey = authKey; + openLoginBindPage(authKey); + }, + // #endif + goAccountLogin() { + switchLoginPage('/pages/users/login/index'); + }, back() { uni.navigateBack(); }, @@ -224,19 +292,22 @@ uni.showLoading({ title: '正在登录中' }); - Routine.getUserProfile() - .then(res => { - Routine.getCode() - .then(code => { - self.getWxUser(code, res); - }) - .catch(res => { - uni.hideLoading(); - }); - }) - .catch(res => { - uni.hideLoading(); - }); + // 先确保开关配置已加载,再发起微信登录,避免 requirePhone 竞态导致误弹手机号 + self.loadMpLoginConfig().finally(() => { + Routine.getUserProfile() + .then(res => { + Routine.getCode() + .then(code => { + self.getWxUser(code, res); + }) + .catch(() => { + uni.hideLoading(); + }); + }) + .catch(() => { + uni.hideLoading(); + }); + }); }, getWxUser(code, res) { @@ -254,17 +325,24 @@ userInfo.type = 'routine' Routine.authUserInfo(userInfo.code, userInfo) .then(res => { - self.authKey = res.data.key; - if (res.data.type === 'register') { + const payload = (res && res.data) ? res.data : (res || {}); + self.authKey = payload.key; + if (payload.type === 'register') { uni.hideLoading(); - self.isPhoneBox = true + self.isPhoneBox = false; + // 开关开启:弹出微信手机号授权;关闭:跳转账号密码绑定页 + if (self.requirePhone) { + self.isPhoneBox = true; + } else { + self.goWechatBindLogin(payload.key); + } } - if (res.data.type === 'login') { + if (payload.type === 'login') { uni.hideLoading(); self.$store.commit('LOGIN', { - token: res.data.token + token: payload.token }); - self.$store.commit("SETUID", res.data.uid); + self.$store.commit("SETUID", payload.uid); self.getUserInfo(); self.$util.Tips({ title: res, diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/Constants.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/Constants.java index 1e15f3a..9ad1a21 100644 --- a/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/Constants.java +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/constants/Constants.java @@ -162,6 +162,8 @@ public class Constants { public static final String CONFIG_KEY_FRONT_API_URL = "front_api_url"; //移动商城接口地址 public static final String CONFIG_KEY_SITE_LOGO = "mobile_top_logo"; //logo地址 public static final String CONFIG_KEY_MOBILE_LOGIN_LOGO = "mobile_login_logo"; // 移动端登录 logo + /** 小程序微信登录是否强制获取手机号:1=开启,0=关闭(默认,走账号密码绑定微信) */ + public static final String CONFIG_KEY_ROUTINE_LOGIN_REQUIRE_PHONE = "routine_login_require_phone"; public static final String CONFIG_KEY_SITE_NAME = "site_name"; //网站名称 public static final String CONFIG_SITE_TENG_XUN_MAP_KEY = "tengxun_map_key"; //腾讯地图key public static final String CONFIG_BANK_LIST = "user_extract_bank"; //可提现银行 diff --git a/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/WxBindingAccountRequest.java b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/WxBindingAccountRequest.java new file mode 100644 index 0000000..40058db --- /dev/null +++ b/crmeb/crmeb-common/src/main/java/com/zbkj/common/request/WxBindingAccountRequest.java @@ -0,0 +1,40 @@ +package com.zbkj.common.request; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.zbkj.common.constants.RegularConstants; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import java.io.Serializable; + +/** + * 小程序微信绑定已有账号请求对象 + * 用于关闭「强制获取手机号」时,通过账号密码将微信 openId 关联到已有用户 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@ApiModel(value = "WxBindingAccountRequest", description = "小程序微信绑定已有账号请求对象") +public class WxBindingAccountRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "账号(手机号)", required = true) + @NotBlank(message = "账号不能为空") + @Pattern(regexp = RegularConstants.PHONE_TWO, message = "请输入正确的手机号") + @JsonProperty("account") + private String account; + + @ApiModelProperty(value = "密码", required = true) + @NotBlank(message = "密码不能为空") + private String password; + + @ApiModelProperty(value = "微信授权 register 阶段返回的 key", required = true) + @NotBlank(message = "key不能为空") + private String key; +} diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java index bdf5371..d6efd6f 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/controller/WeChatController.java @@ -3,6 +3,7 @@ package com.zbkj.front.controller; import com.zbkj.common.model.wechat.TemplateMessage; import com.zbkj.common.request.RegisterAppWxRequest; import com.zbkj.common.request.RegisterThirdUserRequest; +import com.zbkj.common.request.WxBindingAccountRequest; import com.zbkj.common.request.WxBindingPhoneRequest; import com.zbkj.common.response.CommonResult; import com.zbkj.common.response.LoginResponse; @@ -87,6 +88,16 @@ public class WeChatController { return CommonResult.success(userCenterService.registerBindingPhone(request)); } + /** + * 小程序微信绑定已有账号(账号+密码) + * 关闭 routine_login_require_phone 后,新微信用户通过此接口关联已有账号 + */ + @ApiOperation(value = "小程序微信绑定已有账号") + @RequestMapping(value = "/register/binding/account", method = RequestMethod.POST) + public CommonResult registerBindingAccount(@RequestBody @Validated WxBindingAccountRequest request){ + return CommonResult.success(userCenterService.registerBindingAccount(request)); + } + /** * 获取微信公众号js配置 */ @@ -105,6 +116,8 @@ public class WeChatController { public CommonResult> getLogo(){ Map map = new HashMap<>(); map.put("logoUrl", userCenterService.getLogo()); + // 1=微信登录新用户强制获取手机号,0=跳转账号密码绑定微信(默认) + map.put("routineLoginRequirePhone", userCenterService.getRoutineLoginRequirePhone()); return CommonResult.success(map); } diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/UserCenterService.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/UserCenterService.java index 559bd37..b89b2c7 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/UserCenterService.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/UserCenterService.java @@ -173,6 +173,19 @@ public interface UserCenterService extends IService { */ LoginResponse registerBindingPhone(WxBindingPhoneRequest request); + /** + * 小程序微信绑定已有账号(账号+密码) + * @param request 请求参数 + * @return 登录信息 + */ + LoginResponse registerBindingAccount(WxBindingAccountRequest request); + + /** + * 小程序微信登录是否强制获取手机号 + * @return 1=开启,0=关闭 + */ + String getRoutineLoginRequirePhone(); + /** * 用户积分记录列表 * @param pageParamRequest 分页参数 diff --git a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java index 0cb8fca..b5fa412 100644 --- a/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java +++ b/crmeb/crmeb-front/src/main/java/com/zbkj/front/service/impl/UserCenterServiceImpl.java @@ -489,6 +489,16 @@ public class UserCenterServiceImpl extends ServiceImpl implements return systemConfigService.getValueByKey(Constants.CONFIG_KEY_MOBILE_LOGIN_LOGO); } + /** + * 小程序微信登录是否强制获取手机号 + * 系统配置 routine_login_require_phone:1=开启,0=关闭(默认,走账号密码绑定) + */ + @Override + public String getRoutineLoginRequirePhone() { + String value = systemConfigService.getValueByKey(Constants.CONFIG_KEY_ROUTINE_LOGIN_REQUIRE_PHONE); + return StrUtil.isBlank(value) ? "0" : value; + } + /** * 小程序登录 * @param code String 前端临时授权code @@ -854,6 +864,79 @@ public class UserCenterServiceImpl extends ServiceImpl implements return loginResponse; } + /** + * 小程序微信绑定已有账号(账号+密码) + * 关闭「强制获取手机号」时,新微信用户通过已有账号完成关联,下次可直接微信登录 + */ + @Override + public LoginResponse registerBindingAccount(WxBindingAccountRequest request) { + Object cache = redisUtil.get(request.getKey()); + if (ObjectUtil.isNull(cache)) { + throw new CrmebException("微信授权已过期,请重新点击微信登录"); + } + RegisterThirdUserRequest registerThirdUserRequest = JSONObject.parseObject(cache.toString(), RegisterThirdUserRequest.class); + if (!Constants.USER_LOGIN_TYPE_PROGRAM.equals(registerThirdUserRequest.getType())) { + throw new CrmebException("仅支持小程序微信绑定"); + } + + User user = userService.getByPhone(request.getAccount()); + if (ObjectUtil.isNull(user)) { + throw new CrmebException("此账号未注册"); + } + if (!user.getStatus()) { + throw new CrmebException("此账号被禁用"); + } + + String encryptedPassword = CrmebUtil.encryptPassword(request.getPassword(), request.getAccount()); + if (!user.getPwd().equals(encryptedPassword)) { + throw new CrmebException("密码错误"); + } + + UserToken boundByUser = userTokenService.getTokenByUserId(user.getUid(), Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM); + if (ObjectUtil.isNotNull(boundByUser)) { + throw new CrmebException("该账号已绑定微信小程序"); + } + + UserToken boundByOpenId = userTokenService.getByOpenidAndType( + registerThirdUserRequest.getOpenId(), Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM); + if (ObjectUtil.isNotNull(boundByOpenId)) { + throw new CrmebException("该微信已绑定其他账号"); + } + + User finalUser = user; + Boolean execute = transactionTemplate.execute(e -> { + userTokenService.bind( + registerThirdUserRequest.getOpenId(), + Constants.THIRD_LOGIN_TOKEN_TYPE_PROGRAM, + finalUser.getUid()); + return Boolean.TRUE; + }); + if (!execute) { + throw new CrmebException("绑定微信失败,请稍后重试"); + } + + if (ObjectUtil.isNotNull(registerThirdUserRequest.getSpreadPid()) && registerThirdUserRequest.getSpreadPid() > 0) { + loginService.bindSpread(user, registerThirdUserRequest.getSpreadPid()); + } + + user.setLastLoginTime(DateUtil.nowDateTime()); + userService.updateById(user); + redisUtil.delete(request.getKey()); + + LoginResponse loginResponse = new LoginResponse(); + try { + loginResponse.setToken(tokenComponent.createToken(user)); + } catch (Exception e) { + logger.error(StrUtil.format("绑定微信账号后生成token失败,uid={}", user.getUid())); + e.printStackTrace(); + } + loginResponse.setType("login"); + loginResponse.setUid(user.getUid()); + loginResponse.setNikeName(user.getNickname()); + loginResponse.setPhone(user.getPhone()); + return loginResponse; + } + /** * 用户积分记录列表 * @param pageParamRequest 分页参数