feat(1.0.2):修复页面显示问题;修复点击错乱;增加禁止登录

developing
张帅 2 weeks ago
parent e33422867f
commit 4f00b1bd82

@ -84,35 +84,37 @@ func (a *ArticleService) Find(ePhone string, page *page.Page, searchParams map[s
if err != nil {
a.log.Error(err)
}
columnData, err := a.purchaseRepo.FindColumnsByUserId(user.Id)
columnMap := make(map[uint64]struct{})
if len(columnData) != 0 {
for _, v := range columnData {
columnMap[v.ContentId] = struct{}{}
if user != nil {
columnData, err := a.purchaseRepo.FindColumnsByUserId(user.Id)
columnMap := make(map[uint64]struct{})
if len(columnData) != 0 {
for _, v := range columnData {
columnMap[v.ContentId] = struct{}{}
}
}
}
articleIds := make([]uint64, 0, len(articles))
for _, v := range articles {
_, has := columnMap[v.Type]
if has {
purchaseId[v.Id] = struct{}{}
} else {
articleIds = append(articleIds, v.Id)
articleIds := make([]uint64, 0, len(articles))
for _, v := range articles {
_, has := columnMap[v.Type]
if has {
purchaseId[v.Id] = struct{}{}
} else {
articleIds = append(articleIds, v.Id)
}
}
}
purchaseData, err := a.purchaseRepo.FindArticleById(user.Id, articleIds...)
articleIds = nil
if err != nil {
a.log.Error(err.Error())
}
if purchaseData != nil {
for _, v := range purchaseData {
purchaseId[v.ContentId] = struct{}{}
purchaseData, err := a.purchaseRepo.FindArticleById(user.Id, articleIds...)
articleIds = nil
if err != nil {
a.log.Error(err.Error())
}
if purchaseData != nil {
for _, v := range purchaseData {
purchaseId[v.ContentId] = struct{}{}
}
}
}
articleIds = nil
articleIds = nil
}
}
@ -152,6 +154,9 @@ func (a *ArticleService) FindUnLock(ePhone string, page *page.Page, searchParams
a.log.Error(err)
return err
}
if user == nil {
return errors.New("未找到用户")
}
conds := make([]builder.Cond, 0)
class := searchParams["search_eq_class"]
if class != "" {
@ -251,6 +256,10 @@ func (a *ArticleService) Detail(userPhone string, id uint64) (*ArticleDto, error
return nil, err
}
if user == nil {
return nil, errors.New("未找到用户")
}
p, err := a.purchaseRepo.FindByUserIdAndContent(user.Id, id, purchase.ContentTypeArticle)
if err != nil {
a.log.Error(err)

@ -234,6 +234,18 @@ func (c *CaptchaService) GenerateSmsCaptcha(username string, phone string) (*Sms
return nil, errors.New("手机号不能为空")
}
user, err := c.userService.GetUserInfoByPhone(phone)
if err != nil {
c.log.Error(err)
return nil, err
}
if user != nil {
if user.Status == 0 {
return nil, errors.New("您当前暂时不能登录,请联系管理员!")
}
}
// 1. 验证手机号格式
if err := c.VerifyPhoneValid(phone); err != nil {
return nil, err

@ -80,24 +80,6 @@ func (s *Service) GetColumn(ePhone string, name string) (*ColumnDto, error) {
Unlock: unlock,
CreatedAt: time.Time{},
}
if ePhone == "" {
return columnDto, nil
}
userData, err := s.userRepo.FindByPhone(ePhone)
if err != nil {
s.log.Error(err.Error())
return columnDto, nil
}
purchaseData, err := s.purchaseRepo.FindColumnById(userData.Id, col.ID)
if err != nil {
s.log.Error(err)
return columnDto, nil
}
if len(purchaseData) != 0 {
if purchaseData[0].ContentId == col.ID {
columnDto.Unlock = true
}
}
return columnDto, nil
}

@ -102,16 +102,46 @@ func (u *UserService) FindLoginUser(username string) (*UserDto, error) {
}, nil
}
func (u *UserService) GetUserProfileByPhone(ePhone string) (*UserDto, error) {
func (u *UserService) GetUserProfileByePhone(ePhone string) (*UserDto, error) {
user, err := u.repo.FindByPhone(ePhone)
if err != nil {
u.log.Error(err)
return nil, err
}
if user == nil {
return nil, errors.New("未找到用户")
}
p, err := u.phoneEncryption.StringPhone(user.Phone)
if err != nil {
u.log.Error(err)
return nil, err
}
return &UserDto{
Uid: user.Id,
Username: user.Username,
Phone: p,
Password: user.Password != "",
Status: user.Status,
GiftCount: user.GiftCount,
}, nil
}
func (u *UserService) GetUserInfoByPhone(phone string) (*UserDto, error) {
ePhone, err := u.phoneEncryption.Encrypt(phone)
if err != nil {
u.log.Error(err)
return nil, err
}
p, _ := u.phoneEncryption.StringPhone(ePhone)
user, err := u.repo.FindByPhone(ePhone)
if err != nil {
u.log.Error(err)
return nil, err
}
if user == nil {
return nil, nil
}
return &UserDto{
Uid: user.Id,
Username: user.Username,

@ -26,7 +26,7 @@ type UserRepository interface {
FindByID(id uint64) (*User, error)
// FindByPhone 根据手机号查找用户
FindByPhone(phone string) (*User, error)
FindByPhone(ePhone string) (*User, error)
// FindAll 查询用户列表
FindAll(page *page.Page, conds []builder.Cond) error

@ -7,6 +7,7 @@ import (
"cls/pkg/logger"
"cls/pkg/xorm_engine"
"errors"
"fmt"
"xorm.io/builder"
)
@ -63,11 +64,17 @@ func (a AuthRepositoryORM) LoginByCaptcha(phone string) (string, error) {
//注册用户
u.Phone = phone
u.GiftCount = 2
u.Status = 1
_, err = a.engine.Insert(u)
if err != nil {
a.log.Error(err)
}
}
if u.Status == 0 {
return "", errors.New(fmt.Sprintf("用户【%d】禁止登录", u.Id))
}
token, err := a.auth.GenerateUserToken(phone)
if err != nil {
a.log.Error(err)

@ -10,7 +10,7 @@ import (
"xorm.io/builder"
)
//UserRepositoryORM ORM 实现UserRepository 持久化数据
// UserRepositoryORM ORM 实现UserRepository 持久化数据
type UserRepositoryORM struct {
engine *xorm_engine.Engine
log logger.Logger
@ -68,14 +68,14 @@ func (u *UserRepositoryORM) FindByID(id uint64) (*domainUser.User, error) {
return user, nil
}
func (u *UserRepositoryORM) FindByPhone(phone string) (*domainUser.User, error) {
func (u *UserRepositoryORM) FindByPhone(ePhone string) (*domainUser.User, error) {
user := &domainUser.User{}
exist, err := u.engine.Where(builder.Eq{"phone": phone}).Get(user)
exist, err := u.engine.Where(builder.Eq{"phone": ePhone}).Get(user)
if err != nil {
return nil, err
}
if !exist {
return nil, errors.New("用户不存在")
return nil, nil
}
return user, nil

@ -9,8 +9,6 @@ import (
jwt "github.com/appleboy/gin-jwt/v2"
"github.com/gin-gonic/gin"
"net/http"
auth_middleware "cls/internal/infrastructure/middleware/auth"
)
type AuthHandler struct {
@ -97,32 +95,6 @@ func (h *AuthHandler) GetImageCaptcha(c *gin.Context) {
c.JSON(http.StatusOK, captcha)
}
// VerifySmsCaptcha 验证短信验证码
func (h *AuthHandler) VerifySmsCaptcha(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "验证成功"})
}
func (h *AuthHandler) VerifyImageCaptcha(c *gin.Context) {
var req struct {
Token string `json:"token" binding:"required"`
X int `json:"x" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
return
}
// 1. 先验证图片验证码
if b, err := h.captchaService.VerifySlide(req.Token, req.X); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "图片验证码错误"})
return
} else {
c.JSON(http.StatusOK, b)
}
}
func (h *AuthHandler) LoginCaptcha(c *gin.Context) {
var req struct {
Phone string `json:"phone" binding:"required"`
@ -194,34 +166,5 @@ func (h *AuthHandler) RegisterRouters(app gin.IRouter) {
auth.POST("/login-password", h.LoginPassword)
auth.POST("/image-captcha", h.GetImageCaptcha)
auth.POST("/sms-captcha", h.GetSmsCaptcha)
auth.POST("/verify-image", h.VerifyImageCaptcha)
auth.POST("/verify-sms", h.VerifySmsCaptcha)
auth.POST("/decode-token", h.DecodeToken)
}
}
// DecodeToken 解析 token 并返回原始的加密手机号
func (h *AuthHandler) DecodeToken(c *gin.Context) {
var req struct {
Token string `json:"token" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "参数不完整"})
return
}
// 通过 auth 模块获取密钥
secretKey := h.service.GetJWTSecretKey()
// 调用 DecodeTokenStatic 方法解析 token
encryptedPhone, err := auth_middleware.DecodeTokenStatic(req.Token, []byte(secretKey))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"encryptedPhone": encryptedPhone,
})
}

@ -105,7 +105,7 @@ func (u *UserHandler) getProfile(c *gin.Context) {
c.AbortWithStatus(http.StatusInternalServerError)
return
}
userInfo, err := u.service.GetUserProfileByPhone(ePhone)
userInfo, err := u.service.GetUserProfileByePhone(ePhone)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return

@ -23,6 +23,6 @@ export class AppComponent implements OnInit {
private shouldShowTabs(url: string): boolean {
// 在首页和我的页面显示底部导航栏
return url === '/home' || url === '/mine' || url === '/mine/login';
return url=== '/' || url === '/home' || url === '/mine' || url === '/mine/login';
}
}

@ -45,12 +45,14 @@ export class ArticleBuyPage implements OnInit {
}
loadCoupons(){
this.homeService.getCouponList().subscribe(res=>{
res.forEach(res=>{
if(this.getDiscountPrice(this.price.amount,this.price.discount) > res.minAmount){
res.canUse = true
}
})
this.coupons = res;
if(res) {
res.forEach(res=>{
if(this.getDiscountPrice(this.price.amount,this.price.discount) > res.minAmount){
res.canUse = true
}
})
this.coupons = res;
}
})
}
getDiscountPrice(originalPrice: number,discount:number): number {

@ -29,7 +29,19 @@
<div class="stocks-content">
<span class="sc-title">相关个股:</span>
<span class="sc-content">{{ article?.stocks != '' ? article?.stocks : '暂无' }}</span>
<span class="sc-content">
<ng-container *ngIf="article && article.stocks">
<ng-container *ngFor="let stock of article.stocks.split(','); let last = last">
{{stock.trim()}}
<span *ngIf="stockChanges[stock.trim()]"
[class]="'change ' + getStockChangeClass(stock.trim())">
{{formatChange(stockChanges[stock.trim()])}}
</span>
<ng-container *ngIf="!last">, </ng-container>
</ng-container>
</ng-container>
<ng-container *ngIf="!article || !article.stocks">暂无</ng-container>
</span>
</div>
<div class="public-content">
<span class="pb-title">路诚声明:</span>

@ -76,6 +76,19 @@ ion-back-button {
font-size: 12px;
color: #1D1E22;
font-weight: 500;
.change {
margin-left: 4px;
font-weight: 500;
&.up {
color: #F93034;
}
&.down {
color: #07A50B;
}
}
}
}

@ -4,7 +4,7 @@ import { Router } from '@angular/router';
import { ModalController } from '@ionic/angular';
import { Article } from "../../shared/model/article";
import {ImagePreviewComponent} from "./image-preview/image-preview.component";
import { HttpClient } from '@angular/common/http';
@Component({
selector: 'app-article-detail',
@ -15,11 +15,13 @@ import {ImagePreviewComponent} from "./image-preview/image-preview.component";
export class ArticleDetailPage implements OnInit, AfterViewInit {
article?: Article;
weekday = "";
stockChanges: { [key: string]: number } = {};
constructor(
private navCtrl: NavController,
private router: Router,
private modalCtrl: ModalController
private modalCtrl: ModalController,
private http: HttpClient
) {
// 获取导航传递的数据
const navigation = this.router.getCurrentNavigation();
@ -27,6 +29,7 @@ export class ArticleDetailPage implements OnInit, AfterViewInit {
if (article) {
this.article = article;
this.addWeekday();
this.getStockChanges();
}
}
@ -85,4 +88,35 @@ export class ArticleDetailPage implements OnInit, AfterViewInit {
modal.present();
});
}
private getStockChanges() {
if (!this.article?.stocks) return;
const stockCodes = this.article.stocks.split(',').map(code => code.trim());
stockCodes.forEach(code => {
if (code) {
// 调用接口时使用纯数字代码
const numericCode = code.replace(/^(sz|sh)/, '');
this.http.get(`http://api.mairui.club/hsrl/ssjy/${numericCode}/13988152-0AB6-41A9-B2CC-DC9B50250113`)
.subscribe((data: any) => {
if (data && data.pc) {
this.stockChanges[code] = data.pc;
}
});
}
});
}
getStockChangeClass(code: string): string {
const change = this.stockChanges[code];
if (change > 0) return 'up';
if (change < 0) return 'down';
return '';
}
formatChange(change: number): string {
if (change > 0) return `+${change}%`;
return `${change}%`;
}
}

@ -1,12 +1,12 @@
<ion-segment [(ngModel)]="currentSelect" (ionChange)="ionChange($event)">
<ion-segment-button value="new">
<ion-segment-button value="new" (click)="select('new')">
<ion-label>最新</ion-label>
</ion-segment-button>
<ion-segment-button value="unlock">
<ion-label>已解锁</ion-label>
<ion-segment-button value="unlock" (click)="select('unlock')">
<ion-label >已解锁</ion-label>
</ion-segment-button>
<ion-segment-button value="free">
<ion-label>免费试读</ion-label>
<ion-segment-button value="free" (click)="select('free')">
<ion-label >免费试读</ion-label>
</ion-segment-button>
</ion-segment>
@ -22,9 +22,13 @@
<ion-icon name="log-in-outline"></ion-icon>
<p>请登录后查看</p>
</div>
<ion-list *ngIf="username">
<app-article-item [article]="item" [username]="username" *ngFor="let item of data"></app-article-item>
<ion-list *ngIf="username && data.length != 0">
<app-article-item [article]="item" [username]="username" *ngFor="let item of data"></app-article-item>
</ion-list>
<div class="empty-state" *ngIf="data.length==0">
<ion-icon name="map-outline"></ion-icon>
<p>暂无已解锁文章</p>
</div>
</div>
<div *ngIf="currentSelect === 'free'">

@ -5,6 +5,7 @@ import {HomeService} from "../../home.service";
import {getGiftCount, getUser} from "../../../mine/mine.service";
import {Subject, Subscription} from "rxjs";
import {NavigationEnd, Router} from "@angular/router";
import {takeUntil} from "rxjs/operators";
@Component({
selector: 'app-article-content',
@ -18,11 +19,13 @@ export class ArticleContentComponent implements OnInit {
size: 10,
search_eq_class:""
};
hasMore: boolean = true;
hasMore: boolean = false;
@Input() className:string = ""
username:string = ""
data:Article[] = []
currentSelect = "new"
selectBtn:string = "new"
private destroy$ = new Subject<void>();
constructor(private homeService:HomeService,
private cdr:ChangeDetectorRef) { }
@ -32,13 +35,21 @@ export class ArticleContentComponent implements OnInit {
this.getUsername();
}
ionChange(e:any){
}
select(v:string) {
if(v==this.selectBtn){
return
}
this.hasMore = false;
this.selectBtn = v
this.searchParams['page'] = 0
this.data = []
this.currentSelect = e.detail.value
this.destroy$.next()
this.getData()
}
getData() {
switch (this.currentSelect){
case "new":
return this.getNewData()
@ -60,12 +71,14 @@ export class ArticleContentComponent implements OnInit {
}
getNewData() {
this.homeService.list(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
this.homeService.list(this.searchParams).pipe(takeUntil(this.destroy$)).subscribe(res=>{
if(res.items){
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
}
})
}
@ -75,24 +88,28 @@ export class ArticleContentComponent implements OnInit {
if(this.className != "") {
this.searchParams['search_eq_class'] = this.className
}
this.homeService.unlockList(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
this.homeService.unlockList(this.searchParams).pipe(takeUntil(this.destroy$)).subscribe(res=>{
if(res.items) {
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
}
})
}
}
getFreeData() {
this.homeService.freeList(this.searchParams).subscribe(res=>{
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
this.homeService.freeList(this.searchParams).pipe(takeUntil(this.destroy$)).subscribe(res=>{
if(res.items) {
if(res.items.length > 0) {
this.data = this.data.concat(res.items)
this.searchParams['page'] = this.searchParams['page']+1;
this.hasMore = res.items.length === this.searchParams['size'];
this.cdr.detectChanges();
}
}
})
}

@ -4,13 +4,13 @@
<div class="swiper-slide">
<ion-grid>
<ion-row>
<ion-col size="3" *ngFor="let icon of firstPageIcons" (click)="columnDetail(icon.name)">
<ion-col size="3" *ngFor="let icon of firstPageIcons" >
<div class="icon-item">
<div class="icon-circle" [ngClass]="icon.image.replace('.png', '')">
<img [src]="'assets/home/' + icon.image" [alt]="icon.name">
<img (click)="columnDetail(icon.name)" [src]="'assets/home/' + icon.image" [alt]="icon.name">
<span class="hot-tag" *ngIf="icon.remark!=''"> <img [src]="'assets/home/' + icon.remark" ></span>
</div>
<span class="icon-name">{{icon.name}}</span>
<span class="icon-name" (click)="columnDetail(icon.name)">{{icon.name}}</span>
</div>
</ion-col>
</ion-row>

@ -17,6 +17,12 @@
<ion-label>我的优惠券</ion-label>
</ion-item-divider>
<div class="empty-state" *ngIf="!coupons">
<ion-icon name="file-tray-full-outline"></ion-icon>
<p>暂无优惠券</p>
</div>
<div class="coupon-list">
<div class="coupon-item" *ngFor="let coupon of coupons">
<div class="coupon-left">

@ -252,3 +252,25 @@ ion-footer {
}
}
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 20px;
text-align: center;
ion-icon {
font-size: 48px;
color: #999;
margin-bottom: 16px;
}
p {
color: #999;
font-size: 14px;
margin: 0;
}
}

@ -60,7 +60,6 @@ export class MinePage implements OnInit, OnDestroy {
checkLoginStatus() {
getUser().subscribe(res=>{
console.log(res)
if(res.username == "") {
this.isLoggedIn = false;
this.navCtrl.navigateForward("/mine/login")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Loading…
Cancel
Save